Spaces:
Running
Running
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import torch
|
3 |
+
from diffusers import StableDiffusionPipeline, StableDiffusionControlNetPipeline, ControlNetModel, EulerDiscreteScheduler
|
4 |
+
from jinja2 import Template
|
5 |
+
import numpy as np
|
6 |
+
import cv2
|
7 |
+
from PIL import Image
|
8 |
+
import time
|
9 |
+
|
10 |
+
# Load models from Hugging Face
|
11 |
+
sd_model_id = "bhoomikagp/sd2-interior-model-version2" ## test
|
12 |
+
# sd_model_id = "bhoomikagp/sd3-interior-model" ## SD3 model issue loading
|
13 |
+
controlnet_model_id = "lllyasviel/sd-controlnet-mlsd"
|
14 |
+
# Load Stable Diffusion pipeline CUDA
|
15 |
+
scheduler = EulerDiscreteScheduler.from_pretrained(sd_model_id, subfolder="scheduler")
|
16 |
+
sd_pipeline = StableDiffusionPipeline.from_pretrained(sd_model_id, torch_dtype=torch.float16,scheduler=scheduler).to("cuda")
|
17 |
+
# Load ControlNet and Stable Diffusion ControlNet pipeline
|
18 |
+
controlnet = ControlNetModel.from_pretrained(controlnet_model_id, torch_dtype=torch.float16).to("cuda")
|
19 |
+
controlnet_pipeline = StableDiffusionControlNetPipeline.from_pretrained(
|
20 |
+
sd_model_id,
|
21 |
+
controlnet=controlnet,
|
22 |
+
scheduler=scheduler,
|
23 |
+
torch_dtype=torch.float16
|
24 |
+
).to("cuda")
|
25 |
+
|
26 |
+
# choices lists
|
27 |
+
option_choices = ["living_room", "bedroom", "kitchen"]
|
28 |
+
adj_1_choices = [
|
29 |
+
"spacious", "cozy", "minimalist", "elegant", "modern", "rustic",
|
30 |
+
"luxurious", "inviting", "airy", "sophisticated", "bright", "warm",
|
31 |
+
"serene", "chic", "contemporary"
|
32 |
+
]
|
33 |
+
architecture_style_choices = [
|
34 |
+
"modern", "contemporary", "traditional", "industrial", "scandinavian",
|
35 |
+
"mid-century", "colonial", "art deco", "neo-classical", "mediterranean",
|
36 |
+
"gothic", "baroque", "japanese", "brutalist", "tropical"
|
37 |
+
]
|
38 |
+
aesthetic_choices = [
|
39 |
+
"bohemian", "vintage", "minimalist", "luxurious", "eclectic",
|
40 |
+
"mid-century", "art deco", "modern farmhouse", "industrial chic",
|
41 |
+
"shabby chic", "rustic elegance", "coastal", "urban", "transitional",
|
42 |
+
"contemporary classic"
|
43 |
+
]
|
44 |
+
primary_color_choices = [
|
45 |
+
"blue", "green", "beige", "grey", "white", "black",
|
46 |
+
"cream", "brown", "taupe", "burgundy", "mustard",
|
47 |
+
"terracotta", "olive", "peach", "navy"
|
48 |
+
]
|
49 |
+
wood_finish_choices = [
|
50 |
+
"dark oak", "walnut", "mahogany", "teak", "maple",
|
51 |
+
"cherry", "pine", "birch", "ash", "rosewood",
|
52 |
+
"ebony", "cedar", "hickory", "elm", "red oak"
|
53 |
+
]
|
54 |
+
wall_color_choices = [
|
55 |
+
"cream", "off-white", "charcoal", "sage green", "navy blue",
|
56 |
+
"taupe", "light grey", "soft pink", "mustard yellow", "deep teal",
|
57 |
+
"warm beige", "pearl white", "slate blue", "coral", "mint green"
|
58 |
+
]
|
59 |
+
tiles_choices = [
|
60 |
+
"marble", "ceramic", "porcelain", "slate", "wooden-look",
|
61 |
+
"mosaic", "granite", "terracotta", "cement", "quartz",
|
62 |
+
"limestone", "onyx", "travertine", "glass", "encaustic"
|
63 |
+
]
|
64 |
+
|
65 |
+
|
66 |
+
# Templates for each room type
|
67 |
+
templates = {
|
68 |
+
"living_room": """
|
69 |
+
High quality, High resolution, Interior, Architecture, Revit, Autocad, Realistic 3D Render, vray, lumion, raytracing,
|
70 |
+
of a {{ adj_1 }} living room, in {{ architecture_style }} architecture, with {{ aesthetic }} style,
|
71 |
+
painted in {{ wall_color }} with {{ primary_color }} accents, {{ wood_finish }} wood finishes, and {{ tiles }} flooring.
|
72 |
+
""",
|
73 |
+
"bedroom": """
|
74 |
+
High quality, High resolution, Interior, Architecture, Revit, Autocad, Realistic 3D Render, vray, lumion, raytracing,
|
75 |
+
of a {{ adj_1 }} bedroom, in {{ architecture_style }} architecture, with {{ aesthetic }} style,
|
76 |
+
painted in {{ wall_color }} with {{ primary_color }} accents, {{ wood_finish }} wood finishes, and {{ tiles }} flooring.
|
77 |
+
""",
|
78 |
+
"kitchen": """
|
79 |
+
High quality, High resolution, Interior, Architecture, Revit, Autocad, Realistic 3D Render, vray, lumion, raytracing,
|
80 |
+
of a {{ adj_1 }} kitchen, in {{ architecture_style }} architecture, with {{ aesthetic }} style,
|
81 |
+
painted in {{ wall_color }} with {{ primary_color }} accents, {{ wood_finish }} cabinetry, and {{ tiles }} flooring.
|
82 |
+
"""
|
83 |
+
}
|
84 |
+
|
85 |
+
def generate_prompt(option, adj_1, architecture_style, aesthetic, primary_color, wood_finish, wall_color, tiles):
|
86 |
+
# Validate inputs
|
87 |
+
if adj_1 not in adj_1_choices:
|
88 |
+
raise ValueError(f"Invalid adjective: '{adj_1}'. Accepted options are {adj_1_choices}")
|
89 |
+
if architecture_style not in architecture_style_choices:
|
90 |
+
raise ValueError(f"Invalid architecture style: '{architecture_style}'. Accepted options are {architecture_style_choices}")
|
91 |
+
if aesthetic not in aesthetic_choices:
|
92 |
+
raise ValueError(f"Invalid aesthetic style: '{aesthetic}'. Accepted options are {aesthetic_choices}")
|
93 |
+
if primary_color not in primary_color_choices:
|
94 |
+
raise ValueError(f"Invalid primary color: '{primary_color}'. Accepted options are {primary_color_choices}")
|
95 |
+
if wood_finish not in wood_finish_choices:
|
96 |
+
raise ValueError(f"Invalid wood finish: '{wood_finish}'. Accepted options are {wood_finish_choices}")
|
97 |
+
if wall_color not in wall_color_choices:
|
98 |
+
raise ValueError(f"Invalid wall color: '{wall_color}'. Accepted options are {wall_color_choices}")
|
99 |
+
if tiles not in tiles_choices:
|
100 |
+
raise ValueError(f"Invalid tiles: '{tiles}'. Accepted options are {tiles_choices}")
|
101 |
+
|
102 |
+
# Select the template based on the room type option
|
103 |
+
template_str = templates.get(option.lower())
|
104 |
+
if not template_str:
|
105 |
+
raise ValueError(f"Invalid option: '{option}'. Available options are {option_choices}")
|
106 |
+
|
107 |
+
# Render the template
|
108 |
+
template = Template(template_str)
|
109 |
+
return template.render(
|
110 |
+
adj_1=adj_1,
|
111 |
+
architecture_style=architecture_style,
|
112 |
+
aesthetic=aesthetic,
|
113 |
+
primary_color=primary_color,
|
114 |
+
wood_finish=wood_finish,
|
115 |
+
wall_color=wall_color,
|
116 |
+
tiles=tiles
|
117 |
+
)
|
118 |
+
|
119 |
+
# Function to generate initial image
|
120 |
+
def generate_initial_image(n_images=1, option='living_room', adj_1="spacious", architecture_style="modern", aesthetic="minimalist",
|
121 |
+
primary_color="neutral", wood_finish="oak", wall_color="off-white", tiles="marble"):
|
122 |
+
# Generate the prompt from choices
|
123 |
+
gen_prompt = generate_prompt(option, adj_1, architecture_style, aesthetic, primary_color, wood_finish, wall_color, tiles)
|
124 |
+
|
125 |
+
images = [] # List to store generated images along with seeds
|
126 |
+
for i in range(n_images):
|
127 |
+
# Generate a random seed for reproducibility
|
128 |
+
seed = torch.randint(0, 2**32, (1,)).item()
|
129 |
+
generator = torch.manual_seed(seed)
|
130 |
+
|
131 |
+
# Generate the image with the prompt and Stable Diffusion pipeline
|
132 |
+
cfg = 8 # Configuring guidance scale
|
133 |
+
steps = 30 # Number of inference steps
|
134 |
+
width, height = 640, 512
|
135 |
+
# pipeline without controlnet
|
136 |
+
img = sd_pipeline(prompt=gen_prompt, guidance_scale=cfg, num_inference_steps=steps, width=width, height=height, generator=generator).images[0]
|
137 |
+
|
138 |
+
# Append the seed and image to the list for tracking
|
139 |
+
images.append((seed, img))
|
140 |
+
return images
|
141 |
+
|
142 |
+
# Function to modify image using ControlNet pipeline
|
143 |
+
def modify_image_with_controlnet(selected_image, additional_prompt, option='living_room', adj_1="spacious", architecture_style="modern", aesthetic="minimalist",
|
144 |
+
primary_color="brown", wood_finish="walnut", wall_color="beige", tiles="ceramic"):
|
145 |
+
gen_prompt = generate_prompt(option, adj_1, architecture_style, aesthetic, primary_color, wood_finish, wall_color, tiles)
|
146 |
+
gen_prompt += '(('+additional_prompt+'))'
|
147 |
+
seed = selected_image[0]
|
148 |
+
generator = torch.manual_seed(seed)
|
149 |
+
cfg = 8 # Configuring guidance scale
|
150 |
+
steps = 30 # Number of inference steps
|
151 |
+
width, height = 640, 512
|
152 |
+
# preprocessig for control net
|
153 |
+
img = np.array(selected_image[1])
|
154 |
+
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
|
155 |
+
edges = cv2.Canny(gray, 100, 200)
|
156 |
+
edges_image = Image.fromarray(edges)
|
157 |
+
controlnet_strength = 0.85
|
158 |
+
image = controlnet_pipeline(prompt=gen_prompt, image=edges_image, guidance_scale=cfg, width=width, height=height,
|
159 |
+
controlnet_conditioning_scale=controlnet_strength, num_inference_steps=steps, generator=generator).images[0]
|
160 |
+
return image
|
161 |
+
|
162 |
+
# Gradio app logic
|
163 |
+
def generate_image(option, adj_1, architecture_style, aesthetic, primary_color, wood_finish, wall_color, tiles, n_images, progress=gr.Progress()):
|
164 |
+
generated_images = generate_initial_image(
|
165 |
+
n_images=n_images,
|
166 |
+
option=option,
|
167 |
+
adj_1=adj_1,
|
168 |
+
architecture_style=architecture_style,
|
169 |
+
aesthetic=aesthetic,
|
170 |
+
primary_color=primary_color,
|
171 |
+
wood_finish=wood_finish,
|
172 |
+
wall_color=wall_color,
|
173 |
+
tiles=tiles
|
174 |
+
)
|
175 |
+
|
176 |
+
display_images = [img.resize((640, 512)) for _, img in generated_images] # Resize only for display
|
177 |
+
image_identifiers = [f"Image-{i+1}" for i in range(n_images)]
|
178 |
+
|
179 |
+
return display_images, generated_images, image_identifiers
|
180 |
+
|
181 |
+
|
182 |
+
def modify_image(selected_image_id, additional_prompt, option, adj_1, architecture_style, aesthetic, primary_color, wood_finish, wall_color, tiles, images):
|
183 |
+
# Parse selected image identifier to get the index
|
184 |
+
image_index = int(selected_image_id.split("-")[1]) - 1
|
185 |
+
selected_image = images[image_index] # Retrieve the (seed, original_img) tuple without resizing
|
186 |
+
|
187 |
+
# Modify image using ControlNet
|
188 |
+
modified_image = modify_image_with_controlnet(
|
189 |
+
selected_image=selected_image,
|
190 |
+
additional_prompt=additional_prompt,
|
191 |
+
option=option,
|
192 |
+
adj_1=adj_1,
|
193 |
+
architecture_style=architecture_style,
|
194 |
+
aesthetic=aesthetic,
|
195 |
+
primary_color=primary_color,
|
196 |
+
wood_finish=wood_finish,
|
197 |
+
wall_color=wall_color,
|
198 |
+
tiles=tiles
|
199 |
+
)
|
200 |
+
return modified_image
|
201 |
+
|
202 |
+
# Interface
|
203 |
+
with gr.Blocks() as app:
|
204 |
+
gr.Markdown("# Interior Design Image Generation and Modification")
|
205 |
+
|
206 |
+
# Image generation options
|
207 |
+
with gr.Row():
|
208 |
+
option = gr.Radio(label="Room Type", choices=option_choices, value="living_room")
|
209 |
+
adj_1 = gr.Radio(label="Primary Adjective", choices=adj_1_choices, value="spacious")
|
210 |
+
architecture_style = gr.Radio(label="Architecture Style", choices=architecture_style_choices, value="modern")
|
211 |
+
|
212 |
+
with gr.Row():
|
213 |
+
aesthetic = gr.Radio(label="Aesthetic", choices=aesthetic_choices, value="minimalist")
|
214 |
+
primary_color = gr.Radio(label="Primary Color", choices=primary_color_choices, value="neutral")
|
215 |
+
wood_finish = gr.Radio(label="Wood Finish", choices=wood_finish_choices, value="oak")
|
216 |
+
|
217 |
+
with gr.Row():
|
218 |
+
wall_color = gr.Radio(label="Wall Color", choices=wall_color_choices, value="off-white")
|
219 |
+
tiles = gr.Radio(label="Tile Type", choices=tiles_choices, value="marble")
|
220 |
+
|
221 |
+
n_images = gr.Slider(label="Number of Images to Generate", minimum=1, maximum=5, step=1, value=1)
|
222 |
+
|
223 |
+
# Button to generate images
|
224 |
+
generate_btn = gr.Button("Generate Images")
|
225 |
+
output_images = gr.Gallery(label="Generated Images", columns=5) # Display resized images in gallery layout
|
226 |
+
output_generated_images = gr.State() # Hidden state to store original (unmodified) images data
|
227 |
+
output_identifiers = gr.State() # Hidden state for image identifiers
|
228 |
+
|
229 |
+
# Image selection and modification
|
230 |
+
selected_image_id = gr.Radio(label="Select an Image for Modification", choices=[])
|
231 |
+
additional_prompt = gr.Textbox(label="Additional Prompt for Modification", placeholder="OPTIONAL")
|
232 |
+
modify_btn = gr.Button("Modify Image")
|
233 |
+
modified_image = gr.Image(label="Modified Image")
|
234 |
+
|
235 |
+
# Button actions
|
236 |
+
generate_btn.click(
|
237 |
+
fn=generate_image,
|
238 |
+
inputs=[option, adj_1, architecture_style, aesthetic, primary_color, wood_finish, wall_color, tiles, n_images],
|
239 |
+
outputs=[output_images, output_generated_images, output_identifiers]
|
240 |
+
)
|
241 |
+
|
242 |
+
# Update radio button options after images are generated
|
243 |
+
output_images.change(
|
244 |
+
fn=lambda identifiers: gr.update(choices=identifiers, visible=True), # Populate with image identifiers
|
245 |
+
inputs=[output_identifiers],
|
246 |
+
outputs=[selected_image_id]
|
247 |
+
)
|
248 |
+
|
249 |
+
# Trigger modification on button click
|
250 |
+
modify_btn.click(
|
251 |
+
fn=modify_image,
|
252 |
+
inputs=[selected_image_id, additional_prompt, option, adj_1, architecture_style, aesthetic, primary_color, wood_finish, wall_color, tiles, output_generated_images],
|
253 |
+
outputs=modified_image
|
254 |
+
)
|
255 |
+
|
256 |
+
app.launch(debug=True)
|