OVDR (Online Virtual Dressing Room) is a full-stack web application that enables users to virtually try on clothing. It combines user-uploaded full-body images with a selected clothing item to generate a photorealistic try-on result using the StableVITON model. The platform also features intelligent clothing search (powered by CLIP), closet management, and browsing history.
This system aims to improve online fashion shopping experiences through: - AI-powered virtual try-on - Text-based outfit search - Smart closet & history tracking - Personalized recommendations
This system integrates computer vision, recommendation systems, and text-image matching to support a complete virtual fitting experience. It is suitable for online retail platforms looking to enhance user experience and reduce return rates.
Task | Achivements |
---|---|
Web-based application with intuitive UI | React frontend + Flask backend |
Clothing dataset with corresponding text descriptions | Organized dataset (tops, bottoms, dresses) with auto-generated captions |
Functional clothing retrieval and recommendation systems | CLIP-based similarity search and planned collaborative filtering module |
Efficient browsing and try-on with integrated text search | Connects search โ try-on โ save/share experience |
The OVDR project is organized into the following main folders:
backend/
: Flask-based server-side applicationdata/
: All images and user uploadsfrontend/
: React-based user interfacedocs/
: Project documentationwebsite/
: Static HTML website of project๐ OVDR/ (Root Project)
OVDR/
โโโ app.py # Project root entrypoint (if needed)
โโโ README.md # Project overview
โโโ .gitignore # Git ignored files
๐ backend/ โ Flask backend
โโโ backend/ # Flask backend
โ โโโ __init__.py # create_app() and app initialization
โ โโโ config.py # Config class for DB, mail, etc.
โ โโโ exts.py # db/mail/cors plugin initialization
โ โโโ .env # Environment variables for secrets
โ
โ โโโ database/ # DB schema, data and documentation
โ โ โโโ OVDR.sql
โ โ โโโ ovdr_structure.sql
โ โ โโโ ovdr_data_only.sql
โ โ โโโ backup.sql
โ โ โโโ ERD.png # Database Entity-Relationship Diagram
โ โ โโโ database.md # DB documentation
โ
โ โโโ migrations/ # Flask-Migrate migration files
โ
โ โโโ models/ # Model directories
โ โ โโโ clip-vit-large-patch14 # CLIP model
โ โ โโโ StableVITON/ # StableVITON try-on model
โ
โ โโโ routes/ # Flask route blueprints
โ โ โโโ __init__.py
โ โ โโโ auth.py # Login/Register routes
โ โ โโโ closet.py # Clothing listing & closet management
โ โ โโโ combination.py # Outfit combination logic
โ โ โโโ email.py # Email sending routes
โ โ โโโ forms.py # WTForms for validation
โ โ โโโ history.py # Browsing history
โ โ โโโ process.py # Try-on image generation
โ โ โโโ recommend.py # Recommendation system
โ โ โโโ search.py # CLIP-based text search
โ โ โโโ user_image.py # User image upload & fetch
โ
โ โโโ scripts/ # One-time utility scripts
โ โ โโโ image_embedding.py
โ โ โโโ insert_clothes_data.py
โ โ โโโ precompute_similarity.py
โ โ โโโ qwen_generate_caption.py
โ โ โโโ recommend_clicktime.py
โ โ โโโ recommend_sim_algorithm.py
โ โ โโโ rename_clothes.py
โ โ โโโ save_CLIP_model.py
โ โ โโโ search_algorithm.py
โ โ โโโ search_api.py
โ โ โโโ similarity_matrix.npy
โ
โ โโโ utils/ # Utility modules
โ โ โโโ caption_utils.py # Caption formatting and generation
โ โ โโโ download_utils.py # Download cloth image from URL
โ โ โโโ helpers.py # URL/path formatting utilities
โ โ โโโ image_utils.py # Save/rename/delete user images
โ โ โโโ stableviton_runner.py # Run StableVITON subprocess
โ โ โโโ static_serve.py # Static image serving utilities
โ โ โโโ config.py # Duplicate? (backend/config.py used)
โ โ โโโ exts.py # Duplicate? (used for plugin setup)
โ โ โโโ models.py # SQLAlchemy models (User, Clothing...)
โ
โ โโโ similarity_matrix.npy # Precomputed similarity matrix
๐ data/ โ Static & dynamic image data
โโโ data/ # Image assets & user uploads
โ โโโ clothes/ # Main clothing image dataset
โ โ โโโ tops/
โ โ โ โโโ cloth/
โ โ โ โโโ cloth-mask/
โ โ โ โโโ model-tryon/
โ โ โโโ bottoms/
โ โ โโโ dresses/
โ โ โโโ captions/ # Generated captions JSONs
โ
โ โโโ users/
โ โ โโโ image/ # Uploaded user full-body images
โ โ โโโ 1.jpg # Named by user_id
โ
โ โโโ combinations/ # Output try-on images
โ โโโ user_1/
โ โโโ 000001.jpg
๐ frontend/ โ React-based frontend
โโโ frontend/ # React frontend
โ โโโ node_modules/ # Frontend dependencies
โ โโโ public/
โ โโโ src/ # Main frontend code
โ โ โโโ App.js / App.css
โ โ โโโ ClothesDetail.js / .css
โ โ โโโ FullCloset.js / .css
โ โ โโโ History.js / .css
โ โ โโโ Home.js / .css
โ โ โโโ Login.js / .css
โ โ โโโ Register.js
โ โ โโโ SendImage.js / .css
โ โ โโโ TryOn.js / .css
โ โ โโโ UploadImage.js / .css
โ โ โโโ index.js / index.css
โ โ โโโ setupTests.js / reportWebVitals.js
โ โโโ package.json # React project config
โ โโโ package-lock.json
โ โโโ README.md # Frontend description
๐ docs/ โ Project documentation
โโโ docs/ # Project documentation
โ โโโ code_report.md # Final report with structure & logic
โ โโโ api_documentation.md # Full REST API documentation
โ โโโ Summary_of_quality_assurance.pdf # Quality testing doc
๐ website/ โ Static information site
โโโ website/ # Static info website
โ โโโ css/
โ โโโ minutes/
โ โโโ src/
โ โโโ projectindex.html # Presentation site
Please refer to JSdoc at https://csprojects.nottingham.edu.cn/grp-team07-gitlab/grp-team07-gitlab-work/-/tree/main/docs/JSdocs
Each route module encapsulates one set of responsibilities:
user_image.py
: Handle upload/retrieve user photoscloset.py
: Clothing catalog and user closetprocess.py
: Virtual try-on (StableVITON)search.py
: Search with CLIP and text querieshistory.py
: User browsing records recommend.py
: Related outfit recommendation (optional)All static image routing handled by static_serve.py
Database managed via SQLAlchemy + Flask-Migrate
The OVDR backend system is powered by a MySQL-compatible relational database to store users, clothing metadata, try-on results, and behavioral logs. SQLAlchemy ORM is used for seamless object-relational mapping.
Database credentials and server details are managed through environment variables in a .env
file located under ./backend/
, loaded via python-dotenv
. This prevents hardcoding sensitive credentials in source code.
In backend/config.py
, SQLAlchemy URI is constructed as:
SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{USER_NAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
The database schema and sample data are provided under:
backend/database/ovdr_structure.sql
โ full schema (tables, constraints, types)
backend/database/ovdr_data_only.sql
โ data-only insert statements
To initialize a local MySQL database:
mysql -u root -p
CREATE DATABASE ovdr;
USE ovdr;
SOURCE path/to/ovdr_structure.sql;
SOURCE path/to/ovdr_data_only.sql;
Once completed, update .env with:
HOSTNAME=localhost
The following are commonly used IP addresses during internal development:
- 10.176.45.0
: used under eduroam
- 172.19.108.9
: used under UNNC-Living
If your system fails to connect to these, migrate to local MySQL using the above steps.
If you encounter access issues, please reach out to the maintainer via email: ssyzd4@nottingham.edu.cn
.
This section documents all RESTful APIs for the OVDR system. Each subsection corresponds to one module.
routes/auth.py
)Manage user login and registration logic
POST /login
Description: Authenticate user using username and password.
Form Data:
Field | Type | Required | Description |
---|---|---|---|
username | string | Yes | The user's username |
password | string | Yes | Plaintext password input |
Response:
{
"success": "Login successful!",
"username": "testuser",
"user_id": 1
}
Error Responses:
- 400 Bad Request
- Username does not exist
- 400 Bad Request
- Password entered incorrectly
- 400 Bad Request
- Invalid input (form validation failed)
POST /register
Description: Register a new user with a unique username and encrypted password.
Form Data:
Field | Type | Required | Description |
---|---|---|---|
username | string | Yes | Unique username |
password | string | Yes | Password to encrypt |
Response:
{
"message": "User registration succeeded!"
}
Error Response:
{
"error": [["Username already exists."]]
}
GET /captcha/email
Description: Sends a 4-digit verification code to the provided email address. (To be integrated into registration in future)
Query Parameters:
Name | Type | Required | Description |
---|---|---|---|
string | Yes | Email to receive code |
Response:
success
GET /test
Description: Test email sending (hardcoded email). For development/debugging purposes.
Response:
Send successfully
routes/user_image.py
)Upload or retrieve user full-body photos
POST /upload_image
Description: Upload a user full-body image and update the database with the saved path.
Form Data:
Field | Type | Required | Description |
---|---|---|---|
user_id | int | Yes | ID of the user |
file | file | Yes | Image file to be uploaded |
Response:
{
"message": "Image uploaded and path updated successfully",
"image_path": "data/users/image/1.jpg"
}
Errors: - 400: Missing fields or empty file - 404: User not found
GET /get_user_info
Description: Retrieve user profile information including the uploaded image path.
Query Parameters:
Name | Type | Required | Description |
---|---|---|---|
user_id | int | Yes | ID of the user |
Response:
{
"user_id": 1,
"username": "alice",
"image_path": "data/users/image/1.jpg"
}
Errors: - 400: Missing user_id - 404: User not found
routes/closet.py
)Endpoints for browsing and managing virtual clothing closet.
GET /api/clothes
Description: Retrieve all clothing items filtered by category.
Query Parameters:
Name | Type | Required | Description |
---|---|---|---|
category | string | No | Clothing category: tops , bottoms , dresses (default: tops ) |
Response:
{
"message": "Success",
"items": [
{
"id": 1,
"title": "Red Floral Cotton Blouse with v-neckline and short sleeves",
"image_path": "http://localhost:5000/data/clothes/tops/model-tryon/00001.jpg",
"closet_users": 3
}
]
}
GET /detail/<clothing_id>
Description: Retrieve detailed info about a specific clothing item.
Path Parameters:
Name | Type | Description |
---|---|---|
clothing_id | int | Clothing item ID |
Response:
{
"message": "Success",
"item": {
"id": 1,
"labels": ["red", "floral", "cotton", "blouse"],
"title": "Red Floral Cotton Blouse with v-neckline and short sleeves",
"cloth_path": "http://localhost:5000/data/clothes/tops/cloth/00001.jpg"
}
}
POST /add-to-closet
Description: Add a clothing item to a user's closet.
Request Body:
{
"user_id": 1,
"clothing_id": 5
}
Constraints: - A user can have up to 5 items per category. - Duplicate additions are not allowed.
Success Response:
{
"message": "Item added to closet successfully!"
}
Error Responses:
{
"error": "Item already in closet!"
}
{
"error": "Max 5 tops items allowed. Remove one to add new."
}
GET /get-closet
Description: Get all clothing items in a user's closet, optionally filtered by category.
Query Parameters:
Name | Type | Required | Description |
---|---|---|---|
user_id | int | Yes | ID of the user |
category | string | No | Category to filter (default: tops) |
Response:
{
"message": "Success",
"closet": [
{
"id": 5,
"category": "tops",
"url": "http://localhost:5000/data/clothes/tops/cloth/00005.jpg"
}
]
}
POST /remove-from-closet
Description: Remove a clothing item from a user's closet.
Request Body:
{
"user_id": 1,
"clothing_id": 5
}
Response:
{
"message": "Item removed from closet successfully."
}
GET /data/clothes/<path:filename>
Description: Serve static clothing images from the /data/clothes/
directory.
Example Request:
GET /data/clothes/tops/model-tryon/00001.jpg
Response: - Returns the image directly if found.
routes/history.py
)Track and retrieve browsing history
POST /add-history
Description:
Adds a clothing item to the user's browsing history.
- Prevents duplicate entries.
- Keeps only the latest 20 records per user.
Request Body (JSON):
{
"user_id": 1,
"clothing_id": 5
}
Response:
{
"message": "History recorded successfully."
}
Errors:
- 400
: Missing user_id
or clothing_id
.
- 500
: Database error or insertion failure.
GET /get-history
Description:
Retrieves the latest 20 items viewed by a specific user from their browsing history.
Query Parameters:
Name | Type | Required | Description |
---|---|---|---|
user_id | int | Yes | ID of the user |
Example Request:
GET /get-history?user_id=1
Response:
{
"message": "Success",
"history": [
{
"id": 5,
"image": "http://localhost:5000/data/clothes/tops/cloth/00005_top.jpg",
"title": "Red Floral Cotton Blouse with round neckline and long sleeves",
"created_at": "2024-03-30 14:23:12",
"closet_users": 7,
"category": "tops"
}
]
}
Errors:
- 400
: Missing user_id
.
- 500
: Database error or malformed caption field.
routes/process.py
)POST /process_image
Description:
Generate a virtual try-on image by combining a user-uploaded full-body image and a selected clothing item.
You can either:
- Provide item_id
(recommended): automatically fetch clothing from database.
- OR provide cloth_url
directly (e.g., from frontend image selection).
Request Body (JSON):
Field | Type | Required | Description |
---|---|---|---|
user_id | int | โ Yes | ID of the user |
item_id | int | โ No | Closet item ID to fetch clothing from DB |
cloth_url | string | โ No | Optional: URL or path to clothing image |
item_category | string | โ Yes | Type of clothing: "tops", "bottoms", or "dresses" |
โ ๏ธ Either
item_id
orcloth_url
must be provided.
Example:
{
"user_id": 1,
"item_id": 15,
"item_category": "tops"
}
Success Response:
{
"message": "success",
"image_path": "0000012.jpg"
}
Error Responses:
400 Bad Request
: Missing required fields or image404 Not Found
: User or image not found500 Internal Server Error
: StableVITON failure or file I/O issuesGET /data/clothes/<filename>
Description:
Serve static clothing images from the /data/clothes/
directory.
Example:
Request: GET /data/clothes/tops/cloth/000001_top.jpg
Returns: Clothing image file.
routes/search.py
)GET /search
Description:
Search for clothing items using a natural language text query. The backend uses a pretrained CLIP model to match text to clothing image embeddings.
Query Parameters:
Name | Type | Required | Description |
---|---|---|---|
query | string | โ Yes | Natural language query (e.g. "red hoodie") |
top_n | int | โ No | Number of results to return (default: 20) |
Response: Returns an array of matched clothing items ranked by similarity.
{
"items": [
{
"id": 12,
"title": "Red Cotton Hoodie",
"category": "tops",
"image_path": "http://localhost:5000/data/clothes/tops/cloth/00012_top.jpg",
"closet_users": 3
},
...
]
}
Errors:
Code | Description |
---|---|
400 | Missing query parameter |
500 | Internal error (model failure, DB error) |
routes/recommend.py
)GET /clothing/{id}
{
"id": 1,
"name": "White Blouse",
"category": "tops"
}
GET /recommend/{clothing_id}
{
"recommendations": [
{
"id": 12,
"url": "http://localhost:5000/data/clothes/tops/000045_top.jpg"
}
]
}
GET /recommend/popular
{
"recommended_popular": [
{
"id": 88,
"category": "bottoms",
"url": "http://localhost:5000/data/clothes/bottoms/000088_bottom.jpg"
}
]
}
GET /recommend/user/{user_id}
{
"personalized_recommendations": [
{
"id": 73,
"category": "dresses",
"url": "http://localhost:5000/data/clothes/dresses/000073_dress.jpg"
}
]
}
routes/combination.py
)Save try-on results and show outfit combinations
POST /save-combination
Description: Save a virtual try-on combination to the database.
Request Body (JSON):
Field | Type | Required | Description |
---|---|---|---|
user_id | int | โ Yes | ID of the user |
top_id | int | โ No | ID of the top clothing item |
bottom_id | int | โ No | ID of the bottom clothing item |
dress_id | int | โ No | ID of the dress item |
resultImage | string | โ Yes | Relative path to the generated try-on image |
Response Example:
{
"message": "Combination saved!"
}
GET /get-combinations
Description: Retrieve all saved try-on combinations for a given user.
Query Parameters:
Name | Type | Required | Description |
---|---|---|---|
user_id | int | โ Yes | ID of the user |
Response Example:
{
"message": "Success",
"combinations": [
{
"id": 12,
"top_id": 3,
"bottom_id": 5,
"dress_id": null,
"url": "data/combinations/user_1/000023.jpg"
}
]
}
DELETE /delete-combination
Description:
Delete a previously saved outfit combination by its ID.
Request Body:
{
"id": 12
}
Field | Type | Required | Description |
---|---|---|---|
id | int | โ Yes | The ID of the combination to delete |
Response (Success):
{
"message": "Combination deleted successfully."
}
Error Responses:
- 400 Bad Request
:
{ "error": "Missing combination ID" }
404 Not Found
:
{ "error": "Combination not found" }
500 Internal Server Error
:
{ "error": "Database error or deletion failed" }
GET /show_image/<userid>/<filename>
Description: Serve generated outfit image from the /data/combinations/user_<userid>/
folder.
Path Parameters:
Name | Type | Description |
---|---|---|
userid | int | User ID, used to locate the user directory |
filename | string | File name of the image (URL-encoded if needed) |
Example:
GET /show_image/1/000003.jpg
Returns: The image file if found.
routes/email.py
)POST /send-email
Description:
Send a try-on result image to a specified email address. This endpoint expects a base64-encoded image (JPEG) and recipient email.
Request Body:
{
"email": "user@example.com",
"imageBase64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD..."
}
Response: - On success:
{
"success": true,
"message": "Email sent successfully!"
}
{
"success": false,
"message": "Missing email or image"
}
Notes: - Make sure your Flask app is configured with SMTP credentials:
MAILSERVER = "smtp.qq.com"
MAILPORT = 465
MAILUSERNAME = "youremail@qq.com"
MAILPASSWORD = "yourapppassword"
MAILUSE_SSL = True
email_bp
in create_app()
.The OVDR (Online Virtual Dressing Room) database is designed to store user information, virtual try-on data, and clothing items. It supports features like: - User information (user name, hash password and full body image) - Clothing item storage with corresponding text descriptions (captions) - Virtual try-on outfit combinations - User wardrobe (Closet) management - History tracking
The following tables are included in the OVDR database (obsolete or temporarily unused tables are not included):
users
(User Table)Stores user credentials and profile data.
Column | Type | Constraints | Description |
---|---|---|---|
user_id |
INT (PK, AUTO_INCREMENT) | Primary key | Unique ID for each user |
username |
VARCHAR(255) | UNIQUE, NOT NULL | Unique username for login |
password_hash |
VARCHAR(255) | NOT NULL | Hashed password |
image_path |
VARCHAR(255) | DEFAULT NULL | Path to user's full-body image |
clothing
(Clothing Items)Stores clothing data and AI-generated descriptions.
Column | Type | Constraints | Description |
---|---|---|---|
cid |
INT (PK, AUTO_INCREMENT) | Primary key | Unique clothing item ID |
category |
ENUM('tops', 'bottoms', 'dresses') | NOT NULL | Clothing category |
caption |
JSON | NULL | AI-generated clothing description |
closet_users |
INT | DEFAULT 0 | Number of users who added this to wardrobe |
cloth_path |
VARCHAR(255) | NOT NULL | Path to clothing image |
closet
(User Wardrobe)Tracks clothing items added to users' wardrobes.
Column | Type | Constraints | Description |
---|---|---|---|
id |
INT (PK, AUTO_INCREMENT) | Primary key | Record ID |
user_id |
INT | NOT NULL, FK โ users(user_id) |
User who owns this wardrobe |
clothing_id |
INT | NOT NULL, FK โ clothing(cid) ON DELETE CASCADE |
Clothing item |
DELIMITER //
CREATE TRIGGER limit_closet_items
AFTER INSERT ON Closet
FOR EACH ROW
BEGIN
DELETE FROM Closet
WHERE user_id = NEW.user_id
AND id NOT IN (
SELECT id FROM (
SELECT id FROM Closet
WHERE user_id = NEW.user_id
ORDER BY added_at DESC
LIMIT 5
) AS temp_table
);
END;
//
DELIMITER ;
history
(User View History)Tracks which clothing items users have viewed.
Column | Type | Constraints | Description |
---|---|---|---|
id |
INT (PK, AUTO_INCREMENT) | Primary key | Record ID |
user_id |
INT | NOT NULL, FK โ users(user_id) |
User who viewed clothing |
clothing_id |
INT | NOT NULL, FK โ clothing(cid) ON DELETE CASCADE |
Clothing item |
created_at |
TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Timestamp of viewing |
DELIMITER //
CREATE TRIGGER limit_history_records
AFTER INSERT ON `history`
FOR EACH ROW
BEGIN
DELETE FROM `history`
WHERE user_id = NEW.user_id
AND id NOT IN (
SELECT id FROM (
SELECT id FROM history
WHERE user_id = NEW.user_id
ORDER BY created_at DESC
LIMIT 20
) AS temp_table
);
END;
//
DELIMITER ;
./backend/models.py
User
TableField | Type | Constraints | Description |
---|---|---|---|
user_id | Integer | Primary Key | Unique user ID |
username | String | Not Null, Unique | Username for login |
password | String | Not Null | Hashed password |
image_path | String | Nullable | Path to uploaded full-body image |
Clothing
TableField | Type | Constraints | Description |
---|---|---|---|
cid | Integer | Primary Key | Clothing item ID |
category | Enum | Not Null | 'tops', 'bottoms', or 'dresses' |
caption | JSON | Nullable | Structured description of the item |
closet_users | Integer | Default 0 | Number of users who added this to closet |
cloth_path | String | Nullable | Flat cloth image path |
clothmaskpath | String | Nullable | Binary mask path |
modeltryonpath | String | Nullable | Model try-on image path |
Closet
TableField | Type | Constraints | Description |
---|---|---|---|
id | Integer | Primary Key | Closet entry ID |
user_id | Integer | Foreign Key (User) | Owner of this closet item |
clothing_id | Integer | Foreign Key (Clothing) | Clothing item stored |
added_at | TIMESTAMP | Auto Timestamp | Time added to closet |
Combination
TableField | Type | Constraints | Description |
---|---|---|---|
id | Integer | Primary Key | Combination record ID |
user_id | Integer | Foreign Key (User) | User who created this outfit |
top_id | Integer | FK (Clothing, Nullable) | Top clothing item |
bottom_id | Integer | FK (Clothing, Nullable) | Bottom clothing item |
dress_id | Integer | FK (Clothing, Nullable) | Dress clothing item |
outfit_path | String | Not Null | Path to generated try-on image |
created_at | TIMESTAMP | Auto Timestamp | Creation time |
History
TableField | Type | Constraints | Description |
---|---|---|---|
id | Integer | Primary Key | History record ID |
user_id | Integer | Foreign Key (User) | User who viewed the item |
clothing_id | Integer | Foreign Key (Clothing) | Clothing item that was viewed |
created_at | TIMESTAMP | Auto Timestamp | Time of browsing |
StableVITON: Generates try-on results using user photo + garment mask + layout
Input: Absolute paths
Output: Saved to data/combinations/user_
CLIP (OpenAI): Used for text-to-image similarity in search.py
Text queries โ CLIP text embedding
Compared against precomputed image_embeddings.npy
5000
3000
cd frontend
npm install -g concurrently
npm run dev # Starts both backend and frontend
./backend/models/
directoryimage_embeddings.npy
for fast searchThis section outlines both the overall system components and the specific responsibilities undertaken by each team member during development.
create_app
)config.py
)Team Member | Responsibilities | ||
---|---|---|---|
Zixin Ding | Backend architecture, dataset and database, caption generation, user auth, upload, closet and history logic, frontend improvement, bug fixing, Code Report, README, Installation Manual, Final Report | ||
Zhihao Cao | StableVITON try-on module, image I/O handling, bug fixing, Final Report | ||
Peini She | React frontend development, combination and email routing, bug fixing, User Manual writing | ||
Jinghao Liu & Zihan Zhou | Search and Reccomendation module with CLIP, embedding logic, semantic search, Quality Assurance |
Sample SQL schema: ./backend/database/OVDR.sql
Sample try-on output: data/combinations/user_1/*.jpg