Faran Fahandezh
Add application file
ddbbf37
import math
import random
import torch as th
from PIL import Image, ImageDraw
import blobfile as bf
from mpi4py import MPI
import numpy as np
from torch.utils.data import DataLoader, Dataset
from glob import glob
import json
import os
import cv2 as cv
from tqdm import tqdm
from shapely import geometry as gm
from shapely.ops import unary_union
from collections import defaultdict
import copy
def load_rplanhg_data(
batch_size,
analog_bit,
target_set,
set_name='train',
):
"""
For a dataset, create a generator over (shapes, kwargs) pairs.
"""
# set_name = 'train'
set_name = 'eval'
print(f"loading {set_name} of target set {target_set}")
deterministic = False if set_name == 'train' else True
dataset = RPlanhgDataset(set_name, analog_bit, target_set)
if deterministic:
loader = DataLoader(
dataset, batch_size=batch_size, shuffle=False, num_workers=2, drop_last=False
)
else:
loader = DataLoader(
dataset, batch_size=batch_size, shuffle=True, num_workers=2, drop_last=False
)
while True:
yield from loader
def make_non_manhattan(poly, polygon, house_poly):
dist = abs(poly[2] - poly[0])
direction = np.argmin(dist)
center = poly.mean(0)
min = poly.min(0)
max = poly.max(0)
tmp = np.random.randint(3, 7)
new_min_y = center[1] - (max[1] - min[1]) / tmp
new_max_y = center[1] + (max[1] - min[1]) / tmp
if center[0] < 128:
new_min_x = min[0] - (max[0] - min[0]) / np.random.randint(2, 5)
new_max_x = center[0]
poly1 = [[min[0], min[1]], [new_min_x, new_min_y], [new_min_x, new_max_y], [min[0], max[1]], [max[0], max[1]],
[max[0], min[1]]]
else:
new_min_x = center[0]
new_max_x = max[0] + (max[0] - min[0]) / np.random.randint(2, 5)
poly1 = [[min[0], min[1]], [min[0], max[1]], [max[0], max[1]], [new_max_x, new_max_y], [new_max_x, new_min_y],
[max[0], min[1]]]
new_min_x = center[0] - (max[0] - min[0]) / tmp
new_max_x = center[0] + (max[0] - min[0]) / tmp
if center[1] < 128:
new_min_y = min[1] - (max[1] - min[1]) / np.random.randint(2, 5)
new_max_y = center[1]
poly2 = [[min[0], min[1]], [min[0], max[1]], [max[0], max[1]], [max[0], min[1]], [new_max_x, new_min_y],
[new_min_x, new_min_y]]
else:
new_min_y = center[1]
new_max_y = max[1] + (max[1] - min[1]) / np.random.randint(2, 5)
poly2 = [[min[0], min[1]], [min[0], max[1]], [new_min_x, new_max_y], [new_max_x, new_max_y], [max[0], max[1]],
[max[0], min[1]]]
p1 = gm.Polygon(poly1)
iou1 = house_poly.intersection(p1).area / p1.area
p2 = gm.Polygon(poly2)
iou2 = house_poly.intersection(p2).area / p2.area
if iou1 > 0.9 and iou2 > 0.9:
return poly
if iou1 < iou2:
return poly1
else:
return poly2
get_bin = lambda x, z: [int(y) for y in format(x, 'b').zfill(z)]
get_one_hot = lambda x, z: np.eye(z)[min(x, z - 1)]
class RPlanhgDataset(Dataset):
def __init__(self, set_name, analog_bit, target_set, non_manhattan=False):
super().__init__()
base_dir = '../datasets/rplan'
self.non_manhattan = non_manhattan
self.set_name = set_name
self.analog_bit = analog_bit
self.target_set = target_set
self.subgraphs = []
self.org_graphs = []
self.org_houses = []
max_num_points = 100
if self.set_name == 'eval':
cnumber_dist = np.load(f'processed_rplan/rplan_train_{target_set}_cndist.npz', allow_pickle=True)[
'cnumber_dist'].item()
if os.path.exists(f'processed_rplan/rplan_{set_name}_{target_set}.npz'):
data = np.load(f'processed_rplan/rplan_{set_name}_{target_set}.npz', allow_pickle=True)
self.graphs = data['graphs']
self.houses = data['houses']
self.door_masks = data['door_masks']
self.self_masks = data['self_masks']
self.gen_masks = data['gen_masks']
self.num_coords = 2
self.max_num_points = max_num_points
cnumber_dist = np.load(f'processed_rplan/rplan_train_{target_set}_cndist.npz', allow_pickle=True)[
'cnumber_dist'].item()
if self.set_name == 'eval':
data = np.load(f'processed_rplan/rplan_{set_name}_{target_set}_syn.npz', allow_pickle=True)
self.syn_graphs = data['graphs']
self.syn_houses = data['houses']
self.syn_door_masks = data['door_masks']
self.syn_self_masks = data['self_masks']
self.syn_gen_masks = data['gen_masks']
else:
with open(f'{base_dir}/list.txt') as f:
lines = f.readlines()
cnt = 0
# TODO
failed_plans = []
for line in tqdm(lines):
# cnt=cnt+1
# file_name = f'{base_dir}/{line[:-1]}'
# rms_type, fp_eds,rms_bbs,eds_to_rms=reader(file_name)
# fp_size = len([x for x in rms_type if x != 15 and x != 17])
# if self.set_name=='train' and fp_size == target_set:
# continue
# if self.set_name=='eval' and fp_size != target_set:
# continue
# a = [rms_type, rms_bbs, fp_eds, eds_to_rms]
# self.subgraphs.append(a)
# for graph in tqdm(self.subgraphs):
try:
cnt = cnt + 1
file_name = f'{base_dir}/{line[:-1]}'
rms_type, fp_eds, rms_bbs, eds_to_rms = reader(file_name)
fp_size = len([x for x in rms_type if x != 15 and x != 17])
if self.set_name == 'train' and fp_size == target_set:
continue
if self.set_name == 'eval' and fp_size != target_set:
continue
graph = [rms_type, rms_bbs, fp_eds, eds_to_rms]
rms_type = graph[0]
rms_bbs = graph[1]
fp_eds = graph[2]
eds_to_rms = graph[3]
rms_bbs = np.array(rms_bbs)
fp_eds = np.array(fp_eds)
# extract boundary box and centralize
tl = np.min(rms_bbs[:, :2], 0)
br = np.max(rms_bbs[:, 2:], 0)
shift = (tl + br) / 2.0 - 0.5
rms_bbs[:, :2] -= shift
rms_bbs[:, 2:] -= shift
fp_eds[:, :2] -= shift
fp_eds[:, 2:] -= shift
tl -= shift
br -= shift
# build input graph
graph_nodes, graph_edges, rooms_mks = self.build_graph(rms_type, fp_eds, eds_to_rms)
house = []
for room_mask, room_type in zip(rooms_mks, graph_nodes):
room_mask = room_mask.astype(np.uint8)
room_mask = cv.resize(room_mask, (256, 256), interpolation=cv.INTER_AREA)
contours, _ = cv.findContours(room_mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
contours = contours[0]
house.append([contours[:, 0, :], room_type])
self.org_graphs.append(graph_edges)
self.org_houses.append(house)
except IndexError:
# print(line)
failed_plans.append(line)
print("failed: ", failed_plans)
print("len: ", len(failed_plans))
houses = []
door_masks = []
self_masks = []
gen_masks = []
graphs = []
if self.set_name == 'train':
cnumber_dist = defaultdict(list)
if self.non_manhattan:
for h, graph in tqdm(zip(self.org_houses, self.org_graphs), desc='processing dataset'):
# Generating non-manhattan Balconies
tmp = []
for i, room in enumerate(h):
if room[1] > 10:
continue
if len(room[0]) != 4:
continue
if np.random.randint(2):
continue
poly = gm.Polygon(room[0])
house_polygon = unary_union([gm.Polygon(room[0]) for room in h])
room[0] = make_non_manhattan(room[0], poly, house_polygon)
for h, graph in tqdm(zip(self.org_houses, self.org_graphs), desc='processing dataset'):
house = []
corner_bounds = []
num_points = 0
for i, room in enumerate(h):
if room[1] > 10:
room[1] = {15: 11, 17: 12, 16: 13}[room[1]]
room[0] = np.reshape(room[0], [len(room[0]),
2]) / 256. - 0.5 # [[x0,y0],[x1,y1],...,[x15,y15]] and map to 0-1 - > -0.5, 0.5
room[0] = room[0] * 2 # map to [-1, 1]
if self.set_name == 'train':
cnumber_dist[room[1]].append(len(room[0]))
# Adding conditions
num_room_corners = len(room[0])
rtype = np.repeat(np.array([get_one_hot(room[1], 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((room[0], rtype, corner_index, room_index, padding_mask, connections), 1)
house.append(room)
house_layouts = np.concatenate(house, 0)
if len(house_layouts) > max_num_points:
continue
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 in range(len(corner_bounds)):
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], graph).all(1)) or any(np.equal([j, 1, i], graph).all(1)):
door_mask[corner_bounds[i][0]:corner_bounds[i][1],
corner_bounds[j][0]:corner_bounds[j][1]] = 0
houses.append(house_layouts)
door_masks.append(door_mask)
self_masks.append(self_mask)
gen_masks.append(gen_mask)
graphs.append(graph)
self.max_num_points = max_num_points
self.houses = houses
self.door_masks = door_masks
self.self_masks = self_masks
self.gen_masks = gen_masks
self.num_coords = 2
self.graphs = graphs
# --------------
# graph_dict = {f'graph_{i}': graph for i, graph in enumerate(self.graphs)}
for i, graph in enumerate(self.graphs):
print(f"Graph {i}: shape = {np.shape(graph)}, type = {type(graph)}")
# Save each graph individually within a dictionary
# graph_dict = {f'graph_{i}': graph for i, graph in enumerate(self.graphs)}
np.savez_compressed(f'processed_rplan/rplan_{set_name}_{target_set}', graphs=self.graphs,
houses=self.houses,
# np.savez_compressed(f'processed_rplan/rplan_{set_name}_{target_set}', **graph_dict, houses=self.houses,
door_masks=self.door_masks, self_masks=self.self_masks, gen_masks=self.gen_masks)
if self.set_name == 'train':
np.savez_compressed(f'processed_rplan/rplan_{set_name}_{target_set}_cndist', cnumber_dist=cnumber_dist)
if set_name == 'eval':
houses = []
graphs = []
door_masks = []
self_masks = []
gen_masks = []
len_house_layouts = 0
for h, graph in tqdm(zip(self.org_houses, self.org_graphs), desc='processing dataset'):
house = []
corner_bounds = []
num_points = 0
# num_room_corners_total = [cnumber_dist[room[1]][random.randint(0, len(cnumber_dist[room[1]])-1)] for room in h]
# while np.sum(num_room_corners_total)>=max_num_points:
# num_room_corners_total = [cnumber_dist[room[1]][random.randint(0, len(cnumber_dist[room[1]])-1)] for room in h]
num_room_corners_total = []
for room in h:
room_type = room[1]
default_value = 4
if room_type in cnumber_dist and cnumber_dist[room_type]:
num_room_corners_total.append(
cnumber_dist[room_type][random.randint(0, len(cnumber_dist[room_type]) - 1)]
)
else:
# Handle the case where cnumber_dist[room_type] is missing or empty
print(f"Warning: No data found for room type {room_type}. Assigning default value.")
default_value = 4 # Assign a reasonable default value or handle accordingly
num_room_corners_total.append(default_value)
while np.sum(num_room_corners_total) >= max_num_points:
num_room_corners_total = []
for room in h:
room_type = room[1]
if room_type in cnumber_dist and cnumber_dist[room_type]:
num_room_corners_total.append(
cnumber_dist[room_type][random.randint(0, len(cnumber_dist[room_type]) - 1)]
)
else:
num_room_corners_total.append(default_value)
for i, room in enumerate(h):
# Adding conditions
num_room_corners = num_room_corners_total[i]
rtype = np.repeat(np.array([get_one_hot(room[1], 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)
if np.sum([len(room[0]) for room in h]) > max_num_points:
continue
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(h):
if room[1] == 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], graph).all(1)) or any(np.equal([j, 1, i], graph).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
houses.append(house_layouts)
door_masks.append(door_mask)
self_masks.append(self_mask)
gen_masks.append(gen_mask)
graphs.append(graph)
self.syn_houses = houses
self.syn_door_masks = door_masks
self.syn_self_masks = self_masks
self.syn_gen_masks = gen_masks
self.syn_graphs = graphs
np.savez_compressed(f'processed_rplan/rplan_{set_name}_{target_set}_syn', graphs=self.syn_graphs,
houses=self.syn_houses,
door_masks=self.syn_door_masks, self_masks=self.syn_self_masks,
gen_masks=self.syn_gen_masks)
def __len__(self):
return len(self.houses)
def __getitem__(self, idx):
# idx = int(idx//20)
arr = self.houses[idx][:, :self.num_coords]
graph = np.concatenate((self.graphs[idx], np.zeros([200 - len(self.graphs[idx]), 3])), 0)
cond = {
'door_mask': self.door_masks[idx],
'self_mask': self.self_masks[idx],
'gen_mask': self.gen_masks[idx],
'room_types': self.houses[idx][:, self.num_coords:self.num_coords + 25],
'corner_indices': self.houses[idx][:, self.num_coords + 25:self.num_coords + 57],
'room_indices': self.houses[idx][:, self.num_coords + 57:self.num_coords + 89],
'src_key_padding_mask': 1 - self.houses[idx][:, self.num_coords + 89],
'connections': self.houses[idx][:, self.num_coords + 90:self.num_coords + 92],
'graph': graph,
}
if self.set_name == 'eval':
syn_graph = np.concatenate((self.syn_graphs[idx], np.zeros([200 - len(self.syn_graphs[idx]), 3])), 0)
assert (graph == syn_graph).all(), idx
cond.update({
'syn_door_mask': self.syn_door_masks[idx],
'syn_self_mask': self.syn_self_masks[idx],
'syn_gen_mask': self.syn_gen_masks[idx],
'syn_room_types': self.syn_houses[idx][:, self.num_coords:self.num_coords + 25],
'syn_corner_indices': self.syn_houses[idx][:, self.num_coords + 25:self.num_coords + 57],
'syn_room_indices': self.syn_houses[idx][:, self.num_coords + 57:self.num_coords + 89],
'syn_src_key_padding_mask': 1 - self.syn_houses[idx][:, self.num_coords + 89],
'syn_connections': self.syn_houses[idx][:, self.num_coords + 90:self.num_coords + 92],
'syn_graph': syn_graph,
})
if self.set_name == 'train':
#### Random Rotate
rotation = random.randint(0, 3)
if rotation == 1:
arr[:, [0, 1]] = arr[:, [1, 0]]
arr[:, 0] = -arr[:, 0]
elif rotation == 2:
arr[:, [0, 1]] = -arr[:, [1, 0]]
elif rotation == 3:
arr[:, [0, 1]] = arr[:, [1, 0]]
arr[:, 1] = -arr[:, 1]
## To generate any rotation uncomment this
# if self.non_manhattan:
# theta = random.random()*np.pi/2
# rot_mat = np.array([[np.cos(theta), -np.sin(theta), 0],
# [np.sin(theta), np.cos(theta), 0]])
# arr = np.matmul(arr,rot_mat)[:,:2]
# Random Scale
# arr = arr * np.random.normal(1., .5)
# Random Shift
# arr[:, 0] = arr[:, 0] + np.random.normal(0., .1)
# arr[:, 1] = arr[:, 1] + np.random.normal(0., .1)
if not self.analog_bit:
arr = np.transpose(arr, [1, 0])
return arr.astype(float), cond
else:
ONE_HOT_RES = 256
arr_onehot = np.zeros((ONE_HOT_RES * 2, arr.shape[1])) - 1
xs = ((arr[:, 0] + 1) * (ONE_HOT_RES / 2)).astype(int)
ys = ((arr[:, 1] + 1) * (ONE_HOT_RES / 2)).astype(int)
xs = np.array([get_bin(x, 8) for x in xs])
ys = np.array([get_bin(x, 8) for x in ys])
arr_onehot = np.concatenate([xs, ys], 1)
arr_onehot = np.transpose(arr_onehot, [1, 0])
arr_onehot[arr_onehot == 0] = -1
return arr_onehot.astype(float), cond
def make_sequence(self, edges):
polys = []
v_curr = tuple(edges[0][:2])
e_ind_curr = 0
e_visited = [0]
seq_tracker = [v_curr]
find_next = False
while len(e_visited) < len(edges):
if find_next == False:
if v_curr == tuple(edges[e_ind_curr][2:]):
v_curr = tuple(edges[e_ind_curr][:2])
else:
v_curr = tuple(edges[e_ind_curr][2:])
find_next = not find_next
else:
# look for next edge
for k, e in enumerate(edges):
if k not in e_visited:
if (v_curr == tuple(e[:2])):
v_curr = tuple(e[2:])
e_ind_curr = k
e_visited.append(k)
break
elif (v_curr == tuple(e[2:])):
v_curr = tuple(e[:2])
e_ind_curr = k
e_visited.append(k)
break
# extract next sequence
if v_curr == seq_tracker[-1]:
polys.append(seq_tracker)
for k, e in enumerate(edges):
if k not in e_visited:
v_curr = tuple(edges[0][:2])
seq_tracker = [v_curr]
find_next = False
e_ind_curr = k
e_visited.append(k)
break
else:
seq_tracker.append(v_curr)
polys.append(seq_tracker)
return polys
def build_graph(self, rms_type, fp_eds, eds_to_rms, out_size=64):
# create edges
triples = []
nodes = rms_type
# encode connections
for k in range(len(nodes)):
for l in range(len(nodes)):
if l > k:
is_adjacent = any([True for e_map in eds_to_rms if (l in e_map) and (k in e_map)])
if is_adjacent:
if 'train' in self.set_name:
triples.append([k, 1, l])
else:
triples.append([k, 1, l])
else:
if 'train' in self.set_name:
triples.append([k, -1, l])
else:
triples.append([k, -1, l])
# get rooms masks
eds_to_rms_tmp = []
for l in range(len(eds_to_rms)):
eds_to_rms_tmp.append([eds_to_rms[l][0]])
rms_masks = []
im_size = 256
fp_mk = np.zeros((out_size, out_size))
for k in range(len(nodes)):
# add rooms and doors
eds = []
for l, e_map in enumerate(eds_to_rms_tmp):
if (k in e_map):
eds.append(l)
# draw rooms
rm_im = Image.new('L', (im_size, im_size))
dr = ImageDraw.Draw(rm_im)
for eds_poly in [eds]:
poly = self.make_sequence(np.array([fp_eds[l][:4] for l in eds_poly]))[0]
poly = [(im_size * x, im_size * y) for x, y in poly]
if len(poly) >= 2:
dr.polygon(poly, fill='white')
else:
print("Empty room")
exit(0)
rm_im = rm_im.resize((out_size, out_size))
rm_arr = np.array(rm_im)
inds = np.where(rm_arr > 0)
rm_arr[inds] = 1.0
rms_masks.append(rm_arr)
if rms_type[k] != 15 and rms_type[k] != 17:
fp_mk[inds] = k + 1
# trick to remove overlap
for k in range(len(nodes)):
if rms_type[k] != 15 and rms_type[k] != 17:
rm_arr = np.zeros((out_size, out_size))
inds = np.where(fp_mk == k + 1)
rm_arr[inds] = 1.0
rms_masks[k] = rm_arr
# convert to array
nodes = np.array(nodes)
triples = np.array(triples)
rms_masks = np.array(rms_masks)
return nodes, triples, rms_masks
def is_adjacent(box_a, box_b, threshold=0.03):
x0, y0, x1, y1 = box_a
x2, y2, x3, y3 = box_b
h1, h2 = x1 - x0, x3 - x2
w1, w2 = y1 - y0, y3 - y2
xc1, xc2 = (x0 + x1) / 2.0, (x2 + x3) / 2.0
yc1, yc2 = (y0 + y1) / 2.0, (y2 + y3) / 2.0
delta_x = np.abs(xc2 - xc1) - (h1 + h2) / 2.0
delta_y = np.abs(yc2 - yc1) - (w1 + w2) / 2.0
delta = max(delta_x, delta_y)
return delta < threshold
def reader(filename):
with open(filename) as f:
info = json.load(f)
rms_bbs = np.asarray(info['boxes'])
fp_eds = info['edges']
rms_type = info['room_type']
eds_to_rms = info['ed_rm']
s_r = 0
for rmk in range(len(rms_type)):
if (rms_type[rmk] != 17):
s_r = s_r + 1
rms_bbs = np.array(rms_bbs) / 256.0
fp_eds = np.array(fp_eds) / 256.0
fp_eds = fp_eds[:, :4]
tl = np.min(rms_bbs[:, :2], 0)
br = np.max(rms_bbs[:, 2:], 0)
shift = (tl + br) / 2.0 - 0.5
rms_bbs[:, :2] -= shift
rms_bbs[:, 2:] -= shift
fp_eds[:, :2] -= shift
fp_eds[:, 2:] -= shift
tl -= shift
br -= shift
return rms_type, fp_eds, rms_bbs, eds_to_rms
if __name__ == '__main__':
dataset = RPlanhgDataset('eval', False, 8)