Spaces:
Runtime error
Runtime error
Commit
·
dadf8b4
1
Parent(s):
b5a783b
Added the shopping automation logic in a new tab
Browse files- README.md +57 -16
- app.py +115 -1
- requirements.txt +1 -0
README.md
CHANGED
|
@@ -8,11 +8,33 @@ sdk_version: 5.42.0
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
-
|
| 12 |
-
# 🤖 AI-Powered Virtual Try-On
|
| 13 |
|
| 14 |
A portfolio-ready, full-stack AI fashion assistant and virtual try-on system. Combines computer vision, conversational AI, and web automation to deliver style advice, outfit analysis, and shopping recommendations—all in one interactive web app.
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
## 🚀 Features
|
| 17 |
- **Virtual Try-On:** Upload clothing and avatar images to generate realistic try-on results.
|
| 18 |
- **AI Fashion Analysis:** Get style advice and outfit analysis using BLIP and CLIP models.
|
|
@@ -26,24 +48,43 @@ A portfolio-ready, full-stack AI fashion assistant and virtual try-on system. Co
|
|
| 26 |
- **AI Models:** BLIP, CLIP, TinyLlama (all open-source, loaded on demand)
|
| 27 |
- **Automation:** Playwright for web scraping and product search
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
## 🛠️ Setup & Deployment
|
| 30 |
1. All dependencies are listed in `requirements.txt`.
|
| 31 |
2. The backend Flask server is started automatically by `app.py`.
|
| 32 |
3. The Gradio interface is the entry point for Hugging Face Spaces.
|
| 33 |
|
| 34 |
-
##
|
| 35 |
-
-
|
| 36 |
-
-
|
| 37 |
-
-
|
| 38 |
-
|
| 39 |
-
## 🌐 Public Demo
|
| 40 |
-
Deploy this project on [Hugging Face Spaces](https://huggingface.co/spaces) for a live, shareable demo. Free CPU and GPU options available.
|
| 41 |
-
|
| 42 |
-
## 📄 License
|
| 43 |
-
Open-source, for educational and portfolio use. See project root for details.
|
| 44 |
-
|
| 45 |
-
---
|
| 46 |
|
| 47 |
-
|
|
|
|
| 48 |
|
| 49 |
-
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
+
# AI Virtual Try-On & Fashion Advisor (Hugging Face Space)
|
|
|
|
| 12 |
|
| 13 |
A portfolio-ready, full-stack AI fashion assistant and virtual try-on system. Combines computer vision, conversational AI, and web automation to deliver style advice, outfit analysis, and shopping recommendations—all in one interactive web app.
|
| 14 |
|
| 15 |
+
## Features
|
| 16 |
+
|
| 17 |
+
### 1. Try-On & Analysis
|
| 18 |
+
- **Upload Clothing Image & Avatar Image:**
|
| 19 |
+
- Upload two images: one of a clothing item and one of an avatar/person.
|
| 20 |
+
- **AI Try-On Generation:**
|
| 21 |
+
- Uses the [try-on-diffusion API](https://rapidapi.com/hlbq/api/try-on-diffusion/) to generate a virtual try-on result.
|
| 22 |
+
- **BLIP/CLIP AI Analysis:**
|
| 23 |
+
- Automatically analyzes the generated try-on image using BLIP for captioning and fashion prompts.
|
| 24 |
+
- **LLM Fashion Advice:**
|
| 25 |
+
- Provides detailed, context-aware fashion advice using TinyLlama LLM, based on the AI analysis.
|
| 26 |
+
- **All results (image, analysis, advice) are displayed together.**
|
| 27 |
+
|
| 28 |
+
### 2. Chatbot (TinyLlama LLM)
|
| 29 |
+
- **Fashion Chatbot:**
|
| 30 |
+
- Chat with an AI stylist powered by TinyLlama.
|
| 31 |
+
- The chatbot always has context from your latest try-on and analysis, so you can ask about "this outfit" or "these colors" and get relevant answers.
|
| 32 |
+
|
| 33 |
+
### 3. Find Similar
|
| 34 |
+
- **Upload any try-on or fashion image.**
|
| 35 |
+
- **Get recommendations for similar outfits, complementary accessories, and alternative colors.**
|
| 36 |
+
- **Suggested stores are included in the recommendations.**
|
| 37 |
+
|
| 38 |
## 🚀 Features
|
| 39 |
- **Virtual Try-On:** Upload clothing and avatar images to generate realistic try-on results.
|
| 40 |
- **AI Fashion Analysis:** Get style advice and outfit analysis using BLIP and CLIP models.
|
|
|
|
| 48 |
- **AI Models:** BLIP, CLIP, TinyLlama (all open-source, loaded on demand)
|
| 49 |
- **Automation:** Playwright for web scraping and product search
|
| 50 |
|
| 51 |
+
## How It Works
|
| 52 |
+
- All AI models (BLIP, CLIP, TinyLlama) are loaded and run in the backend.
|
| 53 |
+
- The try-on image is generated via an external API (RapidAPI key required).
|
| 54 |
+
- The app is organized into tabs for a clean, modern user experience.
|
| 55 |
+
|
| 56 |
+
## Requirements
|
| 57 |
+
- Python 3.8+
|
| 58 |
+
- `gradio`, `transformers`, `torch`, `Pillow`, `requests`
|
| 59 |
+
- A valid RapidAPI key for try-on-diffusion (replace in `app.py` if needed)
|
| 60 |
+
|
| 61 |
+
## Usage
|
| 62 |
+
1. **Clone this repo and open in Hugging Face Spaces or run locally.**
|
| 63 |
+
2. **Install requirements:**
|
| 64 |
+
```bash
|
| 65 |
+
pip install -r requirements.txt
|
| 66 |
+
```
|
| 67 |
+
3. **Run the app:**
|
| 68 |
+
```bash
|
| 69 |
+
python app.py
|
| 70 |
+
```
|
| 71 |
+
4. **Use the web UI:**
|
| 72 |
+
- Try-On & Analysis: Upload images, generate try-on, get analysis/advice.
|
| 73 |
+
- Chatbot: Ask fashion questions with context.
|
| 74 |
+
- Find Similar: Get recommendations for similar outfits.
|
| 75 |
+
|
| 76 |
## 🛠️ Setup & Deployment
|
| 77 |
1. All dependencies are listed in `requirements.txt`.
|
| 78 |
2. The backend Flask server is started automatically by `app.py`.
|
| 79 |
3. The Gradio interface is the entry point for Hugging Face Spaces.
|
| 80 |
|
| 81 |
+
## Credits
|
| 82 |
+
- BLIP: [Salesforce BLIP](https://huggingface.co/Salesforce/blip-image-captioning-base)
|
| 83 |
+
- CLIP: [OpenAI CLIP](https://huggingface.co/openai/clip-vit-base-patch32)
|
| 84 |
+
- LLM: [TinyLlama](https://huggingface.co/TinyLlama/TinyLlama-1.1B-Chat-v1.0)
|
| 85 |
+
- Try-On API: [try-on-diffusion](https://rapidapi.com/hlbq/api/try-on-diffusion/)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
+
## License
|
| 88 |
+
MIT
|
| 89 |
|
| 90 |
+
---
|
app.py
CHANGED
|
@@ -130,7 +130,6 @@ def analyze_fashion(image):
|
|
| 130 |
advice = llm_advise(sys_prompt, user_msg)
|
| 131 |
return advice
|
| 132 |
|
| 133 |
-
|
| 134 |
# --- Try-On API Integration ---
|
| 135 |
import requests
|
| 136 |
def tryon_api(clothing_img, avatar_img):
|
|
@@ -153,6 +152,115 @@ def tryon_api(clothing_img, avatar_img):
|
|
| 153 |
return Image.open(io.BytesIO(response.content))
|
| 154 |
else:
|
| 155 |
raise Exception('Try-on generation failed: ' + response.text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
|
| 157 |
# --- Gradio UI: Try-On Only ---
|
| 158 |
with gr.Blocks() as demo:
|
|
@@ -232,5 +340,11 @@ with gr.Blocks() as demo:
|
|
| 232 |
sim_btn = gr.Button("Find Similar Outfits")
|
| 233 |
sim_output = gr.Textbox(label="Similar Outfits & Recommendations")
|
| 234 |
sim_btn.click(recommend_similar, inputs=sim_input, outputs=sim_output)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
|
| 236 |
demo.launch()
|
|
|
|
| 130 |
advice = llm_advise(sys_prompt, user_msg)
|
| 131 |
return advice
|
| 132 |
|
|
|
|
| 133 |
# --- Try-On API Integration ---
|
| 134 |
import requests
|
| 135 |
def tryon_api(clothing_img, avatar_img):
|
|
|
|
| 152 |
return Image.open(io.BytesIO(response.content))
|
| 153 |
else:
|
| 154 |
raise Exception('Try-on generation failed: ' + response.text)
|
| 155 |
+
|
| 156 |
+
import asyncio
|
| 157 |
+
import re
|
| 158 |
+
try:
|
| 159 |
+
from playwright.async_api import async_playwright
|
| 160 |
+
except ImportError:
|
| 161 |
+
async_playwright = None
|
| 162 |
+
|
| 163 |
+
FASHION_SITES = {
|
| 164 |
+
'zara': {
|
| 165 |
+
'url': 'https://www.zara.com/us/en/search',
|
| 166 |
+
'search_param': 'searchTerm',
|
| 167 |
+
'selectors': {
|
| 168 |
+
'products': '.product-item',
|
| 169 |
+
'title': '.product-link',
|
| 170 |
+
'price': '.price',
|
| 171 |
+
'image': '.media-image img',
|
| 172 |
+
'link': '.product-link'
|
| 173 |
+
}
|
| 174 |
+
},
|
| 175 |
+
'hm': {
|
| 176 |
+
'url': 'https://www2.hm.com/en_us/search-results.html',
|
| 177 |
+
'search_param': 'q',
|
| 178 |
+
'selectors': {
|
| 179 |
+
'products': '.item-link',
|
| 180 |
+
'title': '.item-heading',
|
| 181 |
+
'price': '.item-price',
|
| 182 |
+
'image': '.item-image img',
|
| 183 |
+
'link': '.item-link'
|
| 184 |
+
}
|
| 185 |
+
},
|
| 186 |
+
'asos': {
|
| 187 |
+
'url': 'https://www.asos.com/us/search/',
|
| 188 |
+
'search_param': 'q',
|
| 189 |
+
'selectors': {
|
| 190 |
+
'products': '[data-testid="product-tile"]',
|
| 191 |
+
'title': '[data-testid="product-title"]',
|
| 192 |
+
'price': '[data-testid="current-price"]',
|
| 193 |
+
'image': 'img',
|
| 194 |
+
'link': 'a'
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
async def search_fashion_items(query, max_results=3):
|
| 200 |
+
results = []
|
| 201 |
+
if async_playwright is None:
|
| 202 |
+
return results
|
| 203 |
+
try:
|
| 204 |
+
async with async_playwright() as p:
|
| 205 |
+
browser = await p.chromium.launch(headless=True)
|
| 206 |
+
context = await browser.new_context(
|
| 207 |
+
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
| 208 |
+
)
|
| 209 |
+
for site_name, site_config in FASHION_SITES.items():
|
| 210 |
+
try:
|
| 211 |
+
page = await context.new_page()
|
| 212 |
+
search_url = f"{site_config['url']}?{site_config['search_param']}={query}"
|
| 213 |
+
await page.goto(search_url, wait_until='networkidle', timeout=10000)
|
| 214 |
+
await page.wait_for_timeout(2000)
|
| 215 |
+
products = await page.query_selector_all(site_config['selectors']['products'])
|
| 216 |
+
site_results = []
|
| 217 |
+
for product in products[:max_results]:
|
| 218 |
+
try:
|
| 219 |
+
title_elem = await product.query_selector(site_config['selectors']['title'])
|
| 220 |
+
price_elem = await product.query_selector(site_config['selectors']['price'])
|
| 221 |
+
image_elem = await product.query_selector(site_config['selectors']['image'])
|
| 222 |
+
link_elem = await product.query_selector(site_config['selectors']['link'])
|
| 223 |
+
title = await title_elem.inner_text() if title_elem else 'N/A'
|
| 224 |
+
price = await price_elem.inner_text() if price_elem else 'N/A'
|
| 225 |
+
image_src = await image_elem.get_attribute('src') if image_elem else ''
|
| 226 |
+
link_href = await link_elem.get_attribute('href') if link_elem else ''
|
| 227 |
+
title = title.strip()[:100]
|
| 228 |
+
price = re.sub(r'[^\\d.,$€£]', '', price) if price != 'N/A' else 'N/A'
|
| 229 |
+
if link_href and not link_href.startswith('http'):
|
| 230 |
+
base_url = f"https://{site_name}.com" if site_name != 'hm' else 'https://www2.hm.com'
|
| 231 |
+
link_href = base_url + link_href
|
| 232 |
+
site_results.append({
|
| 233 |
+
'title': title,
|
| 234 |
+
'price': price,
|
| 235 |
+
'image': image_src,
|
| 236 |
+
'link': link_href,
|
| 237 |
+
'store': site_name.upper(),
|
| 238 |
+
'query': query
|
| 239 |
+
})
|
| 240 |
+
except Exception:
|
| 241 |
+
pass
|
| 242 |
+
results.extend(site_results)
|
| 243 |
+
await page.close()
|
| 244 |
+
except Exception:
|
| 245 |
+
pass
|
| 246 |
+
await browser.close()
|
| 247 |
+
except Exception:
|
| 248 |
+
pass
|
| 249 |
+
return results
|
| 250 |
+
|
| 251 |
+
def shopping_search_sync(query):
|
| 252 |
+
loop = asyncio.new_event_loop()
|
| 253 |
+
asyncio.set_event_loop(loop)
|
| 254 |
+
try:
|
| 255 |
+
results = loop.run_until_complete(search_fashion_items(query, max_results=3))
|
| 256 |
+
finally:
|
| 257 |
+
loop.close()
|
| 258 |
+
if not results:
|
| 259 |
+
return "No products found. Try a different keyword."
|
| 260 |
+
out = ""
|
| 261 |
+
for r in results:
|
| 262 |
+
out += f"\n**{r['title']}**\nStore: {r['store']}\nPrice: {r['price']}\n[View Product]({r['link']})\n\n"
|
| 263 |
+
return out
|
| 264 |
|
| 265 |
# --- Gradio UI: Try-On Only ---
|
| 266 |
with gr.Blocks() as demo:
|
|
|
|
| 340 |
sim_btn = gr.Button("Find Similar Outfits")
|
| 341 |
sim_output = gr.Textbox(label="Similar Outfits & Recommendations")
|
| 342 |
sim_btn.click(recommend_similar, inputs=sim_input, outputs=sim_output)
|
| 343 |
+
|
| 344 |
+
with gr.Tab("Shopping Automation"):
|
| 345 |
+
shop_query = gr.Textbox(label="Describe the item or keywords to search (e.g. 'black dress', 'summer shirt')")
|
| 346 |
+
shop_btn = gr.Button("Search Fashion Stores")
|
| 347 |
+
shop_results = gr.Markdown(label="Shopping Results")
|
| 348 |
+
shop_btn.click(shopping_search_sync, inputs=shop_query, outputs=shop_results)
|
| 349 |
|
| 350 |
demo.launch()
|
requirements.txt
CHANGED
|
@@ -46,3 +46,4 @@ typing-inspection==0.4.1
|
|
| 46 |
typing_extensions==4.14.0
|
| 47 |
urllib3==2.5.0
|
| 48 |
Werkzeug==3.1.3
|
|
|
|
|
|
| 46 |
typing_extensions==4.14.0
|
| 47 |
urllib3==2.5.0
|
| 48 |
Werkzeug==3.1.3
|
| 49 |
+
playwright
|