graph2plan / Network /model /floorplan.py
Zai
test
06db6e9
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