matanzig's picture
Update app.py
55d216b verified
import gradio as gr
import pandas as pd
import numpy as np
import torch
from transformers import CLIPProcessor, CLIPModel
from datasets import load_dataset
from diffusers import StableDiffusionPipeline
from sklearn.metrics.pairwise import cosine_similarity
import warnings
warnings.filterwarnings('ignore')
custom_css = """
body {
background-image: url('https://huggingface.co/spaces/matanzig/Interior-Design-GenAI/resolve/main/viss.jpeg');
background-size: 130% 130%;
background-position: 0% 0%;
background-attachment: fixed;
animation: panBackground 40s ease-in-out infinite alternate;
}
@keyframes panBackground {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
.gradio-container {
background: rgba(20, 20, 20, 0.75) !important;
border-radius: 20px;
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.visionary-title {
font-size: 3.5rem;
font-weight: 900;
text-align: center;
letter-spacing: 4px;
margin-bottom: 5px;
margin-top: 15px;
background: linear-gradient(270deg, #b8860b, #ffd700, #fff8dc, #b8860b);
background-size: 200% 200%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: goldShine 4s ease infinite;
transition: transform 0.3s ease, text-shadow 0.3s ease;
cursor: pointer;
}
.visionary-title:hover {
transform: scale(1.03);
text-shadow: 0px 0px 20px rgba(255, 215, 0, 0.4);
}
@keyframes goldShine {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.visionary-subtitle {
text-align: center;
color: rgba(255, 255, 255, 0.7);
font-size: 1.2rem;
letter-spacing: 2px;
margin-bottom: 30px;
}
.custom-card {
background: rgba(15, 15, 15, 0.6) !important;
border-radius: 20px !important;
border: 1px solid rgba(255, 215, 0, 0.15) !important;
transition: all 0.3s ease-in-out !important;
overflow: hidden !important;
box-shadow: 0 4px 15px rgba(0,0,0,0.4) !important;
}
.custom-card:hover {
transform: translateY(-5px) scale(1.01) !important;
border: 1px solid rgba(255, 215, 0, 0.6) !important;
box-shadow: 0 10px 30px rgba(255, 215, 0, 0.15) !important;
}
.custom-card img {
transition: transform 0.4s ease !important;
}
.custom-card:hover img {
transform: scale(1.05) !important;
}
.custom-score {
background: rgba(10, 10, 10, 0.8) !important;
border-radius: 12px !important;
border: 1px solid rgba(255, 215, 0, 0.1) !important;
transition: all 0.3s ease !important;
}
.custom-score:hover {
border: 1px solid rgba(255, 215, 0, 0.4) !important;
box-shadow: 0 0 15px rgba(255, 215, 0, 0.1) !important;
}
.custom-score:hover textarea {
color: #ffd700 !important;
}
button.primary {
background: linear-gradient(90deg, #151515, #252525) !important;
border: 1px solid rgba(255, 215, 0, 0.4) !important;
color: #ffd700 !important;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: bold !important;
transition: all 0.3s ease !important;
}
button.primary:hover {
background: linear-gradient(90deg, #252525, #353535) !important;
border: 1px solid rgba(255, 215, 0, 0.8) !important;
box-shadow: 0 0 15px rgba(255, 215, 0, 0.2) !important;
transform: translateY(-2px) !important;
}
"""
# --- 1. Load Dataset & Embeddings ---
print("Loading Dataset and Embeddings...")
full_ds = load_dataset("tonijhanel/my_interior_design_dataset", split="train")
sample_dataset = full_ds.shuffle(seed=42).select(range(5000))
df_saved = pd.read_parquet("interior_embeddings.parquet")
dataset_matrix = np.array(df_saved['embedding'].tolist())
# --- 2. Load Deep Learning Models ---
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Loading CLIP Model...")
clip_id = "openai/clip-vit-base-patch32"
processor = CLIPProcessor.from_pretrained(clip_id)
clip_model = CLIPModel.from_pretrained(clip_id).to(device)
print("Loading Stable Diffusion Model...")
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4", torch_dtype=torch.float32)
pipe = pipe.to(device)
pipe.enable_attention_slicing()
# --- 3. Core Engine Logic ---
def get_recommendations_from_vector(user_vector):
similarities = cosine_similarity(user_vector, dataset_matrix)[0]
top_indices = np.argsort(similarities)[-3:][::-1]
top_scores = similarities[top_indices]
recs = [sample_dataset[int(i)]['image'] for i in top_indices]
scores = [f"Match: {score*100:.1f}%" for score in top_scores]
return recs[0], scores[0], recs[1], scores[1], recs[2], scores[2]
def search_by_image(user_image):
if user_image is None:
return None, "", None, "", None, ""
inputs = processor(images=user_image, return_tensors="pt").to(device)
with torch.no_grad():
features = clip_model.get_image_features(**inputs)
if not isinstance(features, torch.Tensor):
features = features.pooler_output if hasattr(features, 'pooler_output') else features[0]
user_vector = features.cpu().numpy().flatten().reshape(1, -1)
return get_recommendations_from_vector(user_vector)
def search_by_text_only(prompt):
if not prompt:
return None, "", None, "", None, ""
inputs = processor(text=[prompt], return_tensors="pt", padding=True).to(device)
with torch.no_grad():
features = clip_model.get_text_features(**inputs)
if not isinstance(features, torch.Tensor):
features = features.pooler_output if hasattr(features, 'pooler_output') else features[0]
user_vector = features.cpu().numpy().flatten().reshape(1, -1)
return get_recommendations_from_vector(user_vector)
def generate_and_recommend(prompt):
if not prompt:
return None, None, "", None, "", None, ""
generated_image = pipe(prompt, num_inference_steps=15).images[0]
inputs = processor(images=generated_image, return_tensors="pt").to(device)
with torch.no_grad():
features = clip_model.get_image_features(**inputs)
if not isinstance(features, torch.Tensor):
features = features.pooler_output if hasattr(features, 'pooler_output') else features[0]
user_vector = features.cpu().numpy().flatten().reshape(1, -1)
rec1, score1, rec2, score2, rec3, score3 = get_recommendations_from_vector(user_vector)
return generated_image, rec1, score1, rec2, score2, rec3, score3
# --- 4. Gradio User Interface (Premium UI) ---
custom_theme = gr.themes.Monochrome(
primary_hue="neutral",
secondary_hue="neutral",
font=[gr.themes.GoogleFont("Montserrat"), "ui-sans-serif", "system-ui", "sans-serif"]
)
with gr.Blocks(title="Visionary | AI Interior Design", css=custom_css, theme=custom_theme) as demo:
gr.HTML("""
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top: 20px;">
<img src="https://huggingface.co/spaces/matanzig/Interior-Design-GenAI/resolve/main/viss1.jpeg"
style="height: 110px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.8); margin-bottom: 10px;">
<h1 class="visionary-title">VISIONARY</h1>
<div class="visionary-subtitle">AI-Powered Interior Design Engine</div>
</div>
""")
with gr.Tabs():
# --- TAB 1: Classic Search by Image ---
with gr.TabItem("๐Ÿ–ผ๏ธ Search by Image"):
gr.Markdown("Upload an inspiration photo to instantly discover visually and stylistically similar rooms from our curated catalog.")
with gr.Row():
with gr.Column(scale=1):
image_input = gr.Image(label="Upload Inspiration", type="pil", elem_classes="custom-card")
img_submit_btn = gr.Button("Find Matches (Instant)", variant="primary")
with gr.Column(scale=2):
with gr.Row():
with gr.Column():
img_rec1 = gr.Image(label="Top Match", elem_classes="custom-card")
img_score1 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
with gr.Column():
img_rec2 = gr.Image(label="2nd Match", elem_classes="custom-card")
img_score2 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
with gr.Column():
img_rec3 = gr.Image(label="3rd Match", elem_classes="custom-card")
img_score3 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
img_submit_btn.click(
fn=search_by_image, inputs=[image_input],
outputs=[img_rec1, img_score1, img_rec2, img_score2, img_rec3, img_score3]
)
# --- TAB 2: Fast Text Search (CLIP Multi-modal) ---
with gr.TabItem("๐Ÿ” Fast Text Search"):
gr.Markdown("Describe a room in text. Our multi-modal vision engine will instantly search the catalog for matching designs.")
with gr.Row():
with gr.Column(scale=1):
fast_text_input = gr.Textbox(label="Search Query", placeholder="e.g., A minimalist industrial bedroom...", lines=3)
fast_txt_submit_btn = gr.Button("Search Catalog (Instant)", variant="primary")
gr.Examples(
examples=[
"A minimalist industrial bedroom with concrete walls",
"Luxury modern bathroom with marble and warm lights",
"Bohemian living room with lots of plants and wood"
],
inputs=fast_text_input
)
with gr.Column(scale=2):
with gr.Row():
with gr.Column():
ft_rec1 = gr.Image(label="Top Match", elem_classes="custom-card")
ft_score1 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
with gr.Column():
ft_rec2 = gr.Image(label="2nd Match", elem_classes="custom-card")
ft_score2 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
with gr.Column():
ft_rec3 = gr.Image(label="3rd Match", elem_classes="custom-card")
ft_score3 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
fast_txt_submit_btn.click(
fn=search_by_text_only, inputs=[fast_text_input],
outputs=[ft_rec1, ft_score1, ft_rec2, ft_score2, ft_rec3, ft_score3]
)
# --- TAB 3: Advanced GenAI Search ---
with gr.TabItem("โœจ AI Design Studio"):
gr.Markdown("Describe your ideal space. Our Generative AI will draft a concept from scratch, and then find the closest real-world equivalents.")
with gr.Row():
with gr.Column(scale=1):
text_input = gr.Textbox(label="Concept Description", placeholder="e.g., A cozy modern living room...", lines=3)
txt_submit_btn = gr.Button("Generate Concept & Match", variant="primary")
gen_output = gr.Image(label="AI Drafted Concept", type="pil", elem_classes="custom-card")
with gr.Column(scale=2):
with gr.Row():
with gr.Column():
txt_rec1 = gr.Image(label="Top Match", elem_classes="custom-card")
txt_score1 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
with gr.Column():
txt_rec2 = gr.Image(label="2nd Match", elem_classes="custom-card")
txt_score2 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
with gr.Column():
txt_rec3 = gr.Image(label="3rd Match", elem_classes="custom-card")
txt_score3 = gr.Textbox(label="Confidence", interactive=False, elem_classes="custom-score")
txt_submit_btn.click(
fn=generate_and_recommend, inputs=[text_input],
outputs=[gen_output, txt_rec1, txt_score1, txt_rec2, txt_score2, txt_rec3, txt_score3]
)
# --- TAB 4: Presentation Video ---
with gr.TabItem("๐ŸŽฅ Presentation Video"):
gr.Markdown("### ๐ŸŽ“ Project Presentation & Walkthrough \nWatch the video below to see a full walkthrough of the dataset, EDA, model pipeline, and the live application.")
gr.Video(value="https://huggingface.co/spaces/matanzig/Interior-Design-GenAI/resolve/main/A3.presentation.mp4", interactive=False)
demo.launch()