Faran Fahandezh
Add application file6
b34bf6c
import gradio as gr
import pandas as pd
import numpy as np
import torch as th
from house_diffusion import dist_util
from house_diffusion.script_util import (
create_model_and_diffusion,
)
from PIL import Image
import io
import drawSvg as drawsvg
import cairosvg
from tqdm import tqdm
import webcolors
import tempfile
from pathlib import Path
import shutil
import os
ROOM_CLASS = {
'Living Room': 1, 'Kitchen': 2, 'Bedroom': 3, 'Bathroom': 4,
'Balcony': 5, 'Entrance': 6, 'Dining Room': 7, 'Study Room': 8,
'Storage': 10, 'Front Door': 11, 'Unknown': 13, 'Interior Door': 12
}
ROOM_CATEGORIES = {
'Living Room': 1, 'Kitchen': 2, 'Bedroom': 3, 'Bathroom': 4,
'Balcony': 5, 'Entrance': 6, 'Dining Room': 7, 'Study Room': 8,
'Storage': 10, 'Front Door': 11, 'Other': 13
}
def save_samples(
sample, ext, model_kwargs,
tmp_count, num_room_types,
# save_gif=False,
save_gif=True,
door_indices=[11, 12, 13], ID_COLOR=None,
is_syn=False, draw_graph=False, save_svg=False, metrics=False):
prefix = 'syn_' if is_syn else ''
graph_errors = []
print(sample.shape)
if not save_gif:
sample = sample[-1:]
for i in tqdm(range(sample.shape[1])):
resolution = 256
images = []
images2 = []
images3 = []
for k in range(sample.shape[0]):
draw_color = drawsvg.Drawing(resolution, resolution, displayInline=False)
draw_color.append(drawsvg.Rectangle(0, 0, resolution, resolution, fill='white'))
polys = []
types = []
for j, point in (enumerate(sample[k][i])):
if model_kwargs[f'{prefix}src_key_padding_mask'][i][j] == 1:
continue
point = point.cpu().data.numpy()
if j == 0:
poly = []
if j > 0 and (model_kwargs[f'{prefix}room_indices'][i, j] != model_kwargs[f'{prefix}room_indices'][
i, j - 1]).any():
polys.append(poly)
types.append(c)
poly = []
pred_center = False
if pred_center:
point = point / 2 + 1
point = point * resolution // 2
else:
point = point / 2 + 0.5
point = point * resolution
poly.append((point[0], point[1]))
c = np.argmax(model_kwargs[f'{prefix}room_types'][i][j - 1].cpu().numpy())
polys.append(poly)
types.append(c)
for poly, c in zip(polys, types):
if c in door_indices or c == 0:
continue
room_type = c
c = webcolors.hex_to_rgb(ID_COLOR[c])
draw_color.append(
drawsvg.Lines(*np.array(poly).flatten().tolist(), close=True, fill=ID_COLOR[room_type],
fill_opacity=1.0, stroke='black', stroke_width=1))
for poly, c in zip(polys, types):
if c not in door_indices:
continue
room_type = c
c = webcolors.hex_to_rgb(ID_COLOR[c])
# TODO --------------------------------------------------------------------------------------
# https://github.com/sakmalh/house_diffusion
line_lengths = [np.linalg.norm(np.array(poly[i]) - np.array(poly[(i + 1) % len(poly)])) for i in
range(len(poly))]
if metrics:
text_size = 5
for z, length in enumerate(line_lengths):
# Calculate the mid-point of the line segment
midpoint = ((poly[z][0] + poly[(z + 1) % len(poly)][0]) / 2,
(poly[z][1] + poly[(z + 1) % len(poly)][1]) / 2)
# Calculate x and y differences
x_diff = poly[z][0] - poly[(z + 1) % len(poly)][0]
y_diff = poly[z][1] - poly[(z + 1) % len(poly)][1]
# Determine text position adjustments based on differences
if int(y_diff) != 0:
if y_diff > 0:
text_x = midpoint[0] + text_size
text_y = midpoint[1]
draw_color.append(drawsvg.Line(
text_x, text_y + text_size, # Start point at the text label
poly[z][0] + text_size, poly[z][1], # End point at the polygon endpoint
stroke='black',
stroke_width=1
))
draw_color.append(drawsvg.Line(
text_x, text_y - text_size, # Start point at the text label
poly[(z + 1) % len(poly)][0] + text_size, poly[(z + 1) % len(poly)][1],
# End point at the polygon endpoint
stroke='black',
stroke_width=1
))
else:
text_x = midpoint[0] - text_size
text_y = midpoint[1]
draw_color.append(drawsvg.Line(
text_x, text_y - text_size, # Start point at the text label
poly[z][0] - text_size, poly[z][1], # End point at the polygon endpoint
stroke='black',
stroke_width=1
))
draw_color.append(drawsvg.Line(
text_x, text_y + text_size, # Start point at the text label
poly[(z + 1) % len(poly)][0] - text_size, poly[(z + 1) % len(poly)][1],
# End point at the polygon endpoint
stroke='black',
stroke_width=1
))
else:
if x_diff > 0:
text_x = midpoint[0]
text_y = midpoint[1] - text_size
draw_color.append(drawsvg.Line(
text_x + text_size, text_y, # Start point at the text label
poly[z][0], poly[z][1] - text_size, # End point at the polygon endpoint
stroke='black',
stroke_width=1
))
draw_color.append(drawsvg.Line(
text_x - text_size, text_y, # Start point at the text label
poly[(z + 1) % len(poly)][0], poly[(z + 1) % len(poly)][1] - text_size,
# End point at the polygon endpoint
stroke='black',
stroke_width=1
))
else:
text_x = midpoint[0]
text_y = midpoint[1] + text_size
draw_color.append(drawsvg.Line(
text_x - text_size, text_y, # Start point at the text label
poly[z][0], poly[z][1] + text_size, # End point at the polygon endpoint
stroke='black',
stroke_width=1
))
draw_color.append(drawsvg.Line(
text_x + text_size, text_y, # Start point at the text label
poly[(z + 1) % len(poly)][0], poly[(z + 1) % len(poly)][1] + text_size,
# End point at the polygon endpoint
stroke='black',
stroke_width=1
))
# Add the text label to the SVG
draw_color.append(
drawsvg.Text(
f'{int(abs(length))}', # Format the length to two decimal places
text_size,
text_x, text_y,
fill='black',
text_anchor='middle',
alignment_baseline='middle'
)
)
draw_color.append(
drawsvg.Lines(*np.array(poly).flatten().tolist(), close=True, fill=ID_COLOR[room_type],
fill_opacity=1.0, stroke='black', stroke_width=1))
if k == sample.shape[0] - 1 or True:
if save_svg:
# draw_color.saveSvg(f'outputs/{ext}/{tmp_count + i}c_{k}_{ext}.svg')
return draw_color
else:
Image.open(io.BytesIO(cairosvg.svg2png(draw_color.asSvg()))).save(
f'outputs/{ext}/{tmp_count + i}c_{ext}.png')
# if save_gif:
# imageio.mimwrite(f'outputs/gif/{tmp_count + i}.gif', images, fps=10, loop=1)
# imageio.mimwrite(f'outputs/gif/{tmp_count + i}_v2.gif', images2, fps=10, loop=1)
# imageio.mimwrite(f'outputs/gif/{tmp_count + i}_v3.gif', images3, fps=10, loop=1)
return graph_errors
def function_test(org_graphs, corners, room_type):
get_one_hot = lambda x, z: np.eye(z)[x]
max_num_points = 100
house = []
corner_bounds = []
num_points = 0
for i, room in enumerate(room_type):
# Adding conditions
num_room_corners = corners[i]
rtype = np.repeat(np.array([get_one_hot(room, 25)]), num_room_corners, 0)
room_index = np.repeat(np.array([get_one_hot(len(house) + 1, 32)]), num_room_corners, 0)
corner_index = np.array([get_one_hot(x, 32) for x in range(num_room_corners)])
# Src_key_padding_mask
padding_mask = np.repeat(1, num_room_corners)
padding_mask = np.expand_dims(padding_mask, 1)
# Generating corner bounds for attention masks
connections = np.array([[i, (i + 1) % num_room_corners] for i in range(num_room_corners)])
connections += num_points
corner_bounds.append([num_points, num_points + num_room_corners])
num_points += num_room_corners
room = np.concatenate((np.zeros([num_room_corners, 2]), rtype, corner_index, room_index,
padding_mask, connections), 1)
house.append(room)
house_layouts = np.concatenate(house, 0)
padding = np.zeros((max_num_points - len(house_layouts), 94))
gen_mask = np.ones((max_num_points, max_num_points))
gen_mask[:len(house_layouts), :len(house_layouts)] = 0
house_layouts = np.concatenate((house_layouts, padding), 0)
door_mask = np.ones((max_num_points, max_num_points))
self_mask = np.ones((max_num_points, max_num_points))
for i, room in enumerate(room_type):
if room == 1:
living_room_index = i
break
for i in range(len(corner_bounds)):
is_connected = False
for j in range(len(corner_bounds)):
if i == j:
self_mask[corner_bounds[i][0]:corner_bounds[i][1], corner_bounds[j][0]:corner_bounds[j][1]] = 0
elif any(np.equal([i, 1, j], org_graphs).all(1)) or any(np.equal([j, 1, i], org_graphs).all(1)):
door_mask[corner_bounds[i][0]:corner_bounds[i][1], corner_bounds[j][0]:corner_bounds[j][1]] = 0
is_connected = True
if not is_connected:
door_mask[corner_bounds[i][0]:corner_bounds[i][1],
corner_bounds[living_room_index][0]:corner_bounds[living_room_index][1]] = 0
syn_houses = house_layouts
syn_door_masks = door_mask
syn_self_masks = self_mask
syn_gen_masks = gen_mask
syn_graph = np.concatenate((org_graphs, np.zeros([200 - len(org_graphs), 3])), 0)
cond = {
'syn_door_mask': syn_door_masks,
'syn_self_mask': syn_self_masks,
'syn_gen_mask': syn_gen_masks,
'syn_room_types': syn_houses[:, 2:2 + 25],
'syn_corner_indices': syn_houses[:, 2 + 25:2 + 57],
'syn_room_indices': syn_houses[:, 2 + 57:2 + 89],
'syn_src_key_padding_mask': 1 - syn_houses[:, 2 + 89],
'syn_connections': syn_houses[:, 2 + 90:2 + 92],
'syn_graph': syn_graph,
}
return cond
def create_layout(graphs, corners, room_type, metrics=False, use_ddim=True, ddim_steps=100, num_samples=4):
model_path = "ckpt/model250000.pt"
steps = f"ddim{ddim_steps}"
args = {
"input_channels": 18,
"condition_channels": 89,
"num_channels": 512,
"out_channels": 2,
"dataset": "rplan",
"use_checkpoint": False,
"use_unet": False,
"learn_sigma": False,
"diffusion_steps": 1000,
"noise_schedule": "cosine",
"timestep_respacing": steps,
"use_kl": False,
"predict_xstart": False,
"rescale_timesteps": False,
"rescale_learned_sigmas": False,
"analog_bit": False,
"target_set": -1,
"set_name": "",
}
dist_util.setup_dist()
model, diffusion = create_model_and_diffusion(
args['input_channels'],
args['condition_channels'],
args['num_channels'],
args['out_channels'],
args['dataset'],
args['use_checkpoint'],
args['use_unet'],
args['learn_sigma'],
args['diffusion_steps'],
args['noise_schedule'],
args['timestep_respacing'],
args['use_kl'],
args['predict_xstart'],
args['rescale_timesteps'],
args['rescale_learned_sigmas'],
args['analog_bit'],
args['target_set'],
args['set_name'],
)
model.load_state_dict(
dist_util.load_state_dict(model_path, map_location="cpu")
)
model.to(dist_util.dev())
model.eval()
ID_COLOR = {1: '#EE4D4D', 2: '#C67C7B', 3: '#FFD274', 4: '#BEBEBE', 5: '#BFE3E8',
6: '#7BA779', 7: '#E87A90', 8: '#FF8C69', 10: '#1F849B', 11: '#727171',
13: '#785A67', 12: '#D3A2C7'}
num_room_types = 14
sample_fn = (diffusion.p_sample_loop if not use_ddim else diffusion.ddim_sample_loop)
print(graphs, corners, room_type)
model_kwargs = function_test(graphs, corners, room_type)
for key in model_kwargs:
# model_kwargs[key] = th.from_numpy(np.array([model_kwargs[key]])).cuda()
model_kwargs[key] = th.from_numpy(np.array([model_kwargs[key]])).cpu()
png_paths = []
svg_paths = []
for count in range(num_samples):
sample = sample_fn(
model,
th.Size([1, 2, 100]),
clip_denoised=True,
model_kwargs=model_kwargs,
)
sample = sample.permute([0, 1, 3, 2])
pred = save_samples(sample, 'pred', model_kwargs, count, num_room_types, ID_COLOR=ID_COLOR,
is_syn=True, draw_graph=False, save_svg=True, save_gif=False, metrics=metrics)
temp_svg_file = tempfile.NamedTemporaryFile(delete=False, suffix=".svg")
pred.saveSvg(temp_svg_file.name)
png_file_name = temp_svg_file.name.split(".")[0].split("/")[-1]
png_file_path = f'./generated_svgs/{png_file_name}.png'
# print(temp_svg_file.name)
# print(png_file_name)
# print(png_file_path)
Image.open(io.BytesIO(cairosvg.svg2png(pred.asSvg()))).save(png_file_path)
output_dir = Path("./generated_svgs")
output_dir.mkdir(parents=True, exist_ok=True)
file_name = temp_svg_file.name.split("/")[-1]
persistent_path = Path(f"{output_dir}/{file_name}")
shutil.move(temp_svg_file.name, persistent_path)
os.chmod(persistent_path, 0o644)
svg_paths.append(str(persistent_path))
png_paths.append(png_file_path)
# print(str(persistent_path))
return png_paths, svg_paths
rooms_data = []
edges_data = []
def generate_layout(metrics: bool, ddim_steps: int, num_samples: int):
room_list = []
room_corners = []
living_room = 0
front_door = False
entrance = -1
print(rooms_data)
print(edges_data)
for i, room in enumerate(rooms_data):
room_list.append(ROOM_CLASS[room['room_type']])
if room['num_corners'] != 0:
room_corners.append(int(room['num_corners']))
else:
room_corners.append(4)
if room['room_type'] == "Living Room":
living_room = i
elif room['room_type'] == "Entrance":
entrance = i
elif room['room_type'] == "Front Door":
front_door = True
edges = []
for edge in edges_data:
source_id = int(edge['room1_id'].split()[0])
target_id = int(edge['room2_id'].split()[0])
edges.append([source_id, 1, target_id])
index = len(room_list)
room_list.append(12)
room_corners.append(4)
edges.append([source_id, 1, index])
edges.append([target_id, 1, index])
if not front_door:
room_list.append(11)
room_corners.append(4)
if entrance == -1:
edges.append([len(room_list) - 1, 1, living_room])
else:
edges.append([len(room_list) - 1, 1, entrance])
if np.sum(room_corners) > 99:
return {"Error": "Number of Corners exceeded"}
print(room_list, room_corners, edges)
png_paths, svg_paths = create_layout(edges, room_corners, room_list, metrics=metrics, ddim_steps=ddim_steps,
num_samples=num_samples)
png_color_guide = './color_guide.png'
rooms_data.clear()
edges_data.clear()
rooms_df = pd.DataFrame(columns=["room_id", "room_type", "num_corners"])
edges_df = pd.DataFrame(columns=["edge_id", "room1_id", "room2_id"])
return png_paths, svg_paths, png_color_guide, rooms_df, edges_df
with gr.Blocks() as demo:
gr.Markdown("## House Layout Generator")
with gr.Row():
room_type = gr.Dropdown(label="Room Type", choices=list(ROOM_CATEGORIES.keys()), value="Living Room")
num_corners = gr.Number(label="Number of Corners", value=4)
add_room_button = gr.Button("Add Room")
with gr.Row():
room1_id = gr.Dropdown(label="Room 1", choices=[], value=None)
room2_id = gr.Dropdown(label="Room 2", choices=[], value=None)
add_edge_button = gr.Button("Add Edge")
rooms_table = gr.DataFrame(label="Rooms Table")
edges_table = gr.DataFrame(label="Edges Table")
metrics_toggle = gr.Checkbox(label="Include metrics", value=True)
ddim_input = gr.Number(label="DDIM steps", value=100)
num_sample = gr.Number(label="Number of samples", value=4)
png_gallery = gr.Gallery(label="Layout PNG Outputs", columns=4)
svg_files = gr.File(label="Layout SVG Outputs (higher quality)")
png_color_guide = gr.Image(label="Color Guide")
def add_room(room_type, num_corners):
room_id = len(rooms_data)
rooms_data.append({
"room_id": room_id,
"room_type": room_type,
"num_corners": num_corners
})
return update_rooms_and_edges()
def add_edge(room1_id, room2_id):
edge_id = len(edges_data)
edges_data.append({
"edge_id": edge_id,
"room1_id": room1_id,
"room2_id": room2_id
})
return update_rooms_and_edges()
def update_rooms_and_edges():
rooms_df = pd.DataFrame(rooms_data, columns=["room_id", "room_type", "num_corners"])
edges_df = pd.DataFrame(edges_data, columns=["edge_id", "room1_id", "room2_id"])
room_options = [f"{room['room_id']} {room['room_type']}" for room in rooms_data]
return rooms_df, edges_df, gr.update(choices=room_options, value=None), gr.update(choices=room_options,
value=None)
generate_button = gr.Button("Generate Layout")
# generate_button.click(generate_layout, inputs=[metrics_toggle, ddim_input, num_sample], outputs=[png_gallery, svg_files, png_color_guide])
generate_button.click(
generate_layout,
inputs=[metrics_toggle, ddim_input, num_sample],
outputs=[
png_gallery,
svg_files,
png_color_guide,
rooms_table,
edges_table
]
)
add_room_button.click(add_room, inputs=[room_type, num_corners],
outputs=[rooms_table, edges_table, room1_id, room2_id])
add_edge_button.click(add_edge, inputs=[room1_id, room2_id], outputs=[rooms_table, edges_table, room1_id, room2_id])
demo.launch()
# global demo
# demo.launch(share=True)