anas
Initial deployment of Raster2Seq floor plan vectorization API
fadb92b
"""
This code is an adaptation that uses Structured 3D for the code base.
Reference: https://github.com/bertjiazheng/Structured3D
"""
import random
import cv2
import numpy as np
from shapely.geometry import Polygon
type2id = {
"living room": 0,
"kitchen": 1,
"bedroom": 2,
"bathroom": 3,
"balcony": 4,
"corridor": 5,
"dining room": 6,
"study": 7,
"studio": 8,
"store room": 9,
"garden": 10,
"laundry room": 11,
"office": 12,
"basement": 13,
"garage": 14,
"undefined": 15,
"door": 16,
"window": 17,
"outwall": -1,
}
def parse_floor_plan_polys(annos):
planes = []
for semantic in annos["semantics"]:
for planeID in semantic["planeID"]:
if annos["planes"][planeID]["type"] == "floor":
planes.append({"planeID": planeID, "type": semantic["type"]})
if semantic["type"] == "outwall":
outerwall_planes = semantic["planeID"]
# extract hole vertices
lines_holes = []
for semantic in annos["semantics"]:
if semantic["type"] in ["window", "door"]:
for planeID in semantic["planeID"]:
lines_holes.extend(np.where(np.array(annos["planeLineMatrix"][planeID]))[0].tolist())
lines_holes = np.unique(lines_holes)
# junctions on the floor
junctions = np.array([junc["coordinate"] for junc in annos["junctions"]])
junction_floor = np.where(np.isclose(junctions[:, -1], 0))[0]
# construct each polygon
polygons = []
for plane in planes:
lineIDs = np.where(np.array(annos["planeLineMatrix"][plane["planeID"]]))[0].tolist()
junction_pairs = [np.where(np.array(annos["lineJunctionMatrix"][lineID]))[0].tolist() for lineID in lineIDs]
polygon = convert_lines_to_vertices(junction_pairs)
polygons.append([polygon[0], plane["type"]])
outerwall_floor = []
for planeID in outerwall_planes:
lineIDs = np.where(np.array(annos["planeLineMatrix"][planeID]))[0].tolist()
lineIDs = np.setdiff1d(lineIDs, lines_holes)
junction_pairs = [np.where(np.array(annos["lineJunctionMatrix"][lineID]))[0].tolist() for lineID in lineIDs]
for start, end in junction_pairs:
if start in junction_floor and end in junction_floor:
outerwall_floor.append([start, end])
outerwall_polygon = convert_lines_to_vertices(outerwall_floor)
polygons.append([outerwall_polygon[0], "outwall"])
return polygons
def convert_lines_to_vertices(lines):
"""
convert line representation to polygon vertices
"""
polygons = []
lines = np.array(lines)
polygon = None
while len(lines) != 0:
if polygon is None:
polygon = lines[0].tolist()
lines = np.delete(lines, 0, 0)
lineID, juncID = np.where(lines == polygon[-1])
vertex = lines[lineID[0], 1 - juncID[0]]
lines = np.delete(lines, lineID, 0)
if vertex in polygon:
polygons.append(polygon)
polygon = None
else:
polygon.append(vertex)
return polygons
def generate_floorplan(
annos,
polygons,
height,
width,
ignore_types,
include_types=None,
fillpoly=True,
constant_color=False,
shuffle=False,
):
"""
plot floorplan
"""
floor_map = np.zeros((height, width))
junctions = np.array([junc["coordinate"][:2] for junc in annos["junctions"]])
room_ind = 0
if shuffle:
room_ind = np.random.randint(0, 2)
polygons_list = []
polygons_type_list = []
for poly_ind, (polygon, poly_type) in enumerate(polygons):
if poly_type in ignore_types:
continue
if include_types is not None and poly_type not in include_types:
continue
polygon = junctions[np.array(polygon)].astype(np.int32)
poly_shapely = Polygon(polygon)
area = poly_shapely.area
# assert area > 10
# if area < 100:
# continue
if poly_type not in ["door", "window"] and area < 100:
continue
if poly_type in ["door", "window"] and area < 1:
continue
if poly_type in ["door", "window"]:
assert polygon.shape[0] == 4
midp_1 = (polygon[0] + polygon[1]) / 2
midp_2 = (polygon[1] + polygon[2]) / 2
midp_3 = (polygon[2] + polygon[3]) / 2
midp_4 = (polygon[3] + polygon[0]) / 2
dist_1_3 = np.square(midp_1 - midp_3).sum()
dist_2_4 = np.square(midp_2 - midp_4).sum()
if dist_1_3 > dist_2_4:
polygon = np.row_stack([midp_1, midp_3])
else:
polygon = np.row_stack([midp_2, midp_4])
polygons_list.append(polygon)
polygons_type_list.append(type2id[poly_type])
if shuffle:
random.shuffle(polygons_list)
for poly_ind, polygon in enumerate(polygons_list):
if shuffle:
room_ind += np.random.randint(1, 2)
else:
room_ind += 1
if fillpoly:
if constant_color:
clr = 1.0
else:
clr = room_ind
cv2.fillPoly(floor_map, [polygon], color=clr)
else:
assert constant_color
cv2.polylines(floor_map, [polygon.astype(np.int32)], isClosed=True, color=1.0, thickness=3)
return floor_map, polygons_list, polygons_type_list