graph2plan / Network /model /floorplan.py
Zai
test
06db6e9
Raw
History Blame Contribute Delete
11.5 kB
import torch
from torch.utils.data import Dataset
import numpy as np
import scipy.io as sio
import cv2
import copy
from model.utils import *
class FloorPlan():
def __init__(self, data, rot=None,fliplr=False):
self.data = copy.deepcopy(data)
''' transform '''
if rot is not None:
theta = self._get_rot()
self.data.gtBoxNew = align_box(self.data.gtBoxNew[:,[1,0,3,2]],theta,rot)[:,[1,0,3,2]]
self.data.boundary[:,[1,0]] = align_points(self.data.boundary[:,[1,0]],theta,rot)
if fliplr:
self.data.gtBoxNew[:,[1,0,3,2]] = fliplr_box(self.data.gtBoxNew[:,[1,0,3,2]])
self.data.boundary[:,[1,0]] = fliplr_2D(self.data.boundary[:,[1,0]])
def _get_rot(self):
door_line = self.data.boundary[:2, :2] # [:,[1,0]]
c = door_line.mean(0) - np.array([127.5,127.5])
theta = np.arctan2(c[1], c[0]) + np.pi # [-pi,pi]
return theta
def get_input_boundary(self, tensor=True):
external = self.data.boundary[:, :2]
door = self.data.boundary[:2, :2]
boundary = np.zeros((128, 128), dtype=float)
inside = np.zeros((128, 128), dtype=float)
front = np.zeros((128, 128), dtype=float)
pts = np.concatenate([external, external[:1]]) // 2
pts_door = door // 2
cv2.fillPoly(inside, pts.reshape(1, -1, 2), 1.0)
cv2.polylines(boundary, pts.reshape(1, -1, 2), True, 1.0, 3)
cv2.polylines(boundary, pts_door.reshape(1, -1, 2), True, 0.5, 3)
cv2.polylines(front, pts_door.reshape(1, -1, 2), True, 1.0, 3)
input_image = np.stack([inside, boundary, front], -1)
if tensor: input_image = torch.tensor(input_image).permute((2, 0, 1)).float()
return input_image
def get_inside_box(self, tensor=True):
boundary = self.data.boundary[:, :2]
X, Y = np.linspace(0, 1, 256), np.linspace(0, 1, 256)
x0, x1 = np.min(boundary[:, 0]), np.max(boundary[:, 0])
y0, y1 = np.min(boundary[:, 1]), np.max(boundary[:, 1])
box = np.array([[X[x0], Y[y0], X[x1], Y[y1]]])
if tensor: box = torch.tensor(box).float()
return box
def get_rooms(self, tensor=True):
rooms = self.data.rType
if tensor: rooms = torch.tensor(rooms).long()
return rooms
def get_attributes(self, gsize=5, alevel=10, relative=True, tensor=True):
boxes = self.data.gtBoxNew[:,[1,0,3,2]]
boundary = self.data.boundary[:,:2]
h, w = 256, 256
if relative:
x0, x1 = np.min(boundary[:, 0]), np.max(boundary[:, 0])+1
y0, y1 = np.min(boundary[:, 1]), np.max(boundary[:, 1])+1
h, w = y1 - y0, x1 - x0
boxes = boxes - np.array([y0, x0, y0, x0], dtype=float)
boxes /= np.array([h, w, h, w])
boxes[:, 2:] -= boxes[:, :2] # y1,x1->h,w
boxes[:, :2] += boxes[:, 2:] / 2 # y0,x0->yc,xc
l = len(boxes)
gbins = np.linspace(0,1,gsize+1) # [1,gsize]
gbins[0],gbins[-1]=-np.inf,np.inf
abins = np.linspace(0,1,alevel+1) # [1,gsize]
abins[0],abins[-1]=-np.inf,np.inf
attributes = np.zeros((l,gsize*gsize+alevel))
# pos: xc*gsize+yc*gsize*gsize
attributes[range(l),(np.digitize(boxes[:,0],gbins)-1)*gsize+np.digitize(boxes[:,1],gbins)-1]=1
# area:(w*h)
attributes[range(l),gsize*gsize+np.digitize(boxes[:,2:].prod(1),abins)-1]=1
if tensor: attributes = torch.tensor(attributes).float()
return attributes
def get_triples(self, random=False, tensor=True):
boxes = self.data.gtBoxNew[:, [1, 0, 3, 2]]
vocab = get_vocab()
triples = []
# add edge relation
for u, v, _ in self.data.rEdge:
# Todo: random order for edge
# if random and np.random.random() > 0.5:
# u, v = v, u
uy0, ux0, uy1, ux1 = boxes[u]
vy0, vx0, vy1, vx1 = boxes[v]
uc = (uy0 + uy1) / 2, (ux0 + ux1) / 2
vc = (vy0 + vy1) / 2, (vx0 + vx1) / 2
# surrounding/inside -> X four quadrants
if ux0 < vx0 and ux1 > vx1 and uy0 < vy0 and uy1 > vy1:
relation = 'surrounding'
elif ux0 >= vx0 and ux1 <= vx1 and uy0 >= vy0 and uy1 <= vy1:
relation = 'inside'
else:
relation = point_box_relation(uc, boxes[v])
triples.append([u, vocab['pred_name_to_idx'][relation], v])
triples = np.array(triples, dtype=int)
if tensor: triples = torch.tensor(triples).long()
return triples
def get_layout_image(self,tensor=True):
img = np.full((128,128),13,dtype=np.uint8)
boundary = self.data.boundary[:,:2]
boundary = np.concatenate([boundary, boundary[:1]])
cv2.fillPoly(img, (boundary//2).reshape(1, -1, 2), 0)
order = self.data.order-1
rType = self.data.rType[order]
rBox = self.data.gtBoxNew[order]
for i in range(len(rType)):
t = rType[i]
if t==0: continue
b = rBox[i]//2
img[b[1]:b[3],b[0]:b[2]]=t
cv2.polylines(img, (boundary//2).reshape(1, -1, 2), True,14)
if tensor: img = torch.tensor(img).long()
return img
def get_boxes(self,relative=True,tensor=True):
boxes = self.data.gtBoxNew[:, [1, 0, 3, 2]]
boundary = self.data.boundary[:,:2]
X,Y = np.linspace(0,1,256),np.linspace(0,1,256)
if relative:
x0, x1 = np.min(boundary[:, 0]), np.max(boundary[:, 0])+1
y0, y1 = np.min(boundary[:, 1]), np.max(boundary[:, 1])+1
h, w = y1 - y0, x1 - x0
X,Y = np.linspace(0,1,w),np.linspace(0,1,h)
boxes = boxes-np.array([y0,x0,y0,x0])
norm = lambda box:np.array([
X[max(box[1],0)],
Y[max(box[0],0)],
X[min(box[3]-1,w-1)],
Y[min(box[2]-1,h-1)]
])
boxes = np.apply_along_axis(norm,1,boxes)
boxes[:,2:]-=boxes[:,:2]
boxes[:,:2]+=boxes[:,2:]/2
if tensor:
boxes = torch.tensor(boxes).float()
return boxes
def get_inside_coords(self,size=(32,32),tensor=True):
h,w = size
X = np.linspace(0,1,w)
Y = np.linspace(0,1,h)
img = np.zeros(size)
boundary = self.data.boundary[:,:2]
boundary = boundary*np.array(size)//256
cv2.fillPoly(img, boundary.reshape(1, -1, 2), 1)
coords = np.where(img>0)
coords = np.stack((X[coords[1]],Y[coords[0]]),1)
if tensor: coords = torch.tensor(coords).unsqueeze(0).float()
return coords
def get_test_data(self, tensor=True):
name = self.data.name
boundary = self.get_input_boundary(tensor=tensor)
inside_box = self.get_inside_box(tensor=tensor)
rooms = self.get_rooms(tensor=tensor)
attrs = self.get_attributes(tensor=tensor)
triples = self.get_triples(random=False, tensor=tensor)
return boundary, inside_box, rooms, attrs, triples, name
def get_train_data(self, tensor=True):
name = self.data.name
boundary = self.get_input_boundary(tensor=tensor)
inside_box = self.get_inside_box(tensor=tensor)
rooms = self.get_rooms(tensor=tensor)
attrs = self.get_attributes(tensor=tensor)
triples = self.get_triples(random=False, tensor=tensor)
# gt
layout = self.get_layout_image(tensor=tensor)
boxes = self.get_boxes(tensor=tensor)
# constrains
inside_coords = self.get_inside_coords(tensor=tensor)
return boundary,inside_box,rooms,attrs,triples,layout,boxes,inside_coords,name
class FloorPlanDataset(Dataset):
def __init__(self,data_path):
self.data = sio.loadmat(data_path, squeeze_me=True, struct_as_record=False)['data']
self.train = True if 'train' in data_path else False
def __len__(self):
return len(self.data)
def __getitem__(self,i):
if self.train:
rot = np.random.randint(0,4)
fliplr = np.random.random()>0.5
fp = FloorPlan(self.data[i],rot=rot,fliplr=fliplr)
return fp.get_train_data()
else:
fp = FloorPlan(self.data[i])
return fp.get_train_data()
def floorplan_collate_fn(batch):
all_boundary = []
all_inside_box = []
all_objs = []
all_attrs = []
all_triples = []
all_layout = []
all_boxes = []
all_inside_coords = []
all_obj_to_img = []
all_triple_to_img = []
all_name = []
obj_offset = 0
for i, (
boundary,
inside_box,
rooms,
attrs,
triples,
layout,
boxes,
inside_coords,
name
) in enumerate(batch):
if rooms.dim() == 0 or triples.dim() == 0:
continue
O, T = rooms.size(0), triples.size(0)
all_boundary.append(boundary[None])
all_inside_box.append(inside_box)
all_objs.append(rooms)
all_attrs.append(attrs)
triples = triples.clone()
triples[:, 0] += obj_offset
triples[:, 2] += obj_offset
all_triples.append(triples)
all_layout.append(layout[None])
all_boxes.append(boxes)
all_inside_coords.append(inside_coords)
all_name.append(name)
all_obj_to_img.append(torch.LongTensor(O).fill_(i))
all_triple_to_img.append(torch.LongTensor(T).fill_(i))
obj_offset += O
all_boundary = torch.cat(all_boundary)
all_inside_box = torch.cat(all_inside_box)
all_objs = torch.cat(all_objs)
all_attrs = torch.cat(all_attrs)
all_triples = torch.cat(all_triples)
all_layout = torch.cat(all_layout)
all_boxes = torch.cat(all_boxes)
all_obj_to_img = torch.cat(all_obj_to_img)
all_triple_to_img = torch.cat(all_triple_to_img)
out = (
all_boundary,
all_inside_box,
all_objs,
all_attrs,
all_triples,
all_layout,
all_boxes,
all_inside_coords,
all_obj_to_img,
all_triple_to_img,
all_name
)
return out
def vis_fp(fp,size=(256,256)):
cmap = get_color_map()
img = np.full((256, 256, 4), 0, dtype=np.uint8)
boundary = fp.data.boundary[:,:2]
boundary = np.concatenate([boundary, boundary[:1]])
door = fp.data.boundary[:2, :2]
c = cmap[0].tolist()
cv2.fillPoly(img, boundary.reshape(1, -1, 2), (c[0],c[1],c[2],255))
order = fp.data.order-1
rType = fp.data.rType[order]
rBox = fp.data.gtBoxNew[order]
for i in range(len(rType)):
t = rType[i]
if t==0: continue
b = rBox[i]
c = cmap[t].tolist()
cv2.rectangle(img, (b[0],b[1]),(b[2]-1,b[3]-1), (c[0],c[1],c[2],255),-1)
c = cmap[-1].tolist()
cv2.rectangle(img, (b[0],b[1]),(b[2]-1,b[3]-1), (c[0],c[1],c[2],255),3)
cv2.putText(img,f"{i}",((b[0]+b[2])//2,(b[1]+b[3])//2),cv2.FONT_HERSHEY_PLAIN,1,(0,0,0,255))
c = cmap[-3].tolist()
cv2.polylines(img, boundary.reshape(1, -1, 2), True, (c[0],c[1],c[2],255),3)
c = cmap[-2].tolist()
cv2.polylines(img, door.reshape(1, -1, 2), True, (c[0],c[1],c[2],255),3)
if size!=(256,256): return cv2.resize(img,size)
return img
if __name__ == "__main__":
pass