import torch import scipy.io as sio import numpy as np import cv2 import copy from model.utils import * class FloorPlan(): def __init__(self, data, train=False, rot=None): self.data = copy.deepcopy(data) self._get_rot() if rot is not None: if train: boxes = self.data.box[:, :4][:, [1, 0, 3, 2]] boxes = align_box(boxes, self.rot, rot)[:, [1, 0, 3, 2]] self.data.box[:, :4] = boxes points = self.data.boundary[:, :2][:, [1, 0]] points = align_points(points, self.rot, rot)[:, [1, 0]] self.data.boundary[:, :2] = points self._get_rot() 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] self.rot = 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): external = self.data.boundary[:, :2] X, Y = np.linspace(0, 1, 256), np.linspace(0, 1, 256) x0, x1 = np.min(external[:, 0]), np.max(external[:, 0]) y0, y1 = np.min(external[:, 1]), np.max(external[:, 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.box[:, -1] if tensor: rooms = torch.tensor(rooms).long() return rooms def get_attributes(self, gsize=5, alevel=10, relative=True, tensor=True): boxes = self.data.box[:, :4][:, [1, 0, 3, 2]] external = self.data.boundary h, w = 256, 256 if relative: external = np.asarray(external) x0, x1 = np.min(external[:, 0]), np.max(external[:, 0]) y0, y1 = np.min(external[:, 1]), np.max(external[:, 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.box[:, :4][:, [1, 0, 3, 2]] triples = [] # add edge relation for u, v, _ in self.data.edge: 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 vis_box(self): h, w = 128, 128 image = np.full((h, w, 4), 0, dtype=np.uint8) boxes = self.data.box[:, :4] // 2 objs = self.data.box[:, -1] for i, obj in enumerate(objs): if obj == 14: continue color = colormap_255[obj] box = boxes[i] cv2.rectangle(image, (box[0], box[1]), (box[2], box[3]), (color[0], color[1], color[2], 255), 3) return image def get_test_data(self, tensor=True): 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 def adapt_graph(self, fp_graph): fp = FloorPlan(fp_graph.data, train=True, rot=self.rot) g_external = fp.data.boundary[:, :2] gx0, gx1 = np.min(g_external[:, 0]), np.max(g_external[:, 0]) gy0, gy1 = np.min(g_external[:, 1]), np.max(g_external[:, 1]) gw, gh = gx1 - gx0, gy1 - gy0 fp.data.boundary = self.data.boundary b_external = self.data.boundary[:, :2] bx0, bx1 = np.min(b_external[:, 0]), np.max(b_external[:, 0]) by0, by1 = np.min(b_external[:, 1]), np.max(b_external[:, 1]) bh, bw = by1 - by0, bx1 - bx0 box_adapter = lambda box: (((box - np.array([gx0, gy0, gx0, gy0])) * np.array([bw, bh, bw, bh])) / np.array( [gw, gh, gw, gh]) + np.array([bx0, by0, bx0, by0])).astype(int) fp.data.box[:, :4] = np.apply_along_axis(box_adapter, 1, fp.data.box[:, :4]) return fp def adjust_graph(self): external = self.data.boundary[:, :2] bx0, bx1 = np.min(external[:, 0]), np.max(external[:, 0]) by0, by1 = np.min(external[:, 1]), np.max(external[:, 1]) hw_b = np.array([by1 - by0, bx1 - bx0]) step = hw_b / 10 pts = np.concatenate([external, external[:1]]) mask = np.zeros((256, 256), dtype=np.uint8) cv2.fillPoly(mask, pts.reshape(1, -1, 2), 255) # plt.imshow(mask) # plt.show() mask = cv2.resize(mask[by0:by1 + 1, bx0:bx1 + 1], (10, 10)) # plt.imshow(mask) # plt.show() mask[mask > 0] = 255 outside_rooms = [] for i in range(len(self.data.box)): box = self.data.box[i][:4][[1, 0, 3, 2]] center = (box[:2] + box[2:]) / 2 center55 = ((center - np.array([by0, bx0])) * 10 / hw_b).astype(int) if not mask[center55[0], center55[1]]: outside_rooms.append([i, center55]) candicate_coords55 = {} for i, coords55 in outside_rooms: row, col = coords55 # left/right/up/down candicate_coords55[i] = np.array([ next((col-c for c in range(col,-1,-1) if mask[row,c]==255),255), next((c-col for c in range(col+1,5) if mask[row,c]==255),255), next((row-r for r in range(row,-1,-1) if mask[r,col]==255),255), next((r-row for r in range(row+1,5) if mask[r,col]==255),255)]) signs = np.array([ [0, -1, 0, -1], [0, 1, 0, 1], [-1, 0, -1, 0], [1, 0, 1, 0] ]) for i, coords55 in outside_rooms: deltas = candicate_coords55[i] idx = np.argmin(deltas) self.data.box[i, :4] += (signs[idx] * deltas[idx] * np.tile(step, 2)).astype(int)[[1, 0, 3, 2]] if __name__ == "__main__": pass