Spaces:
Runtime error
Runtime error
| import cv2 | |
| import numpy as np | |
| from scipy import ndimage | |
| def extract_regions(adj_mat, corners, corner_sorted): | |
| all_regions = list() | |
| cur_idx = 0 | |
| corners = corners.astype(np.int) | |
| nb_orders = _sort_neighours(adj_mat, corners) | |
| while cur_idx is not None: | |
| regions = _get_regions_for_corner(cur_idx, adj_mat, nb_orders) | |
| all_regions.extend(regions) | |
| cur_idx = _get_new_start(adj_mat, cur_idx, corners) | |
| outwall_idx = get_outwall(all_regions, corners, corner_sorted) | |
| all_regions.pop(outwall_idx) | |
| # all_regions = filter_regions(all_regions) # only used for drawing visualization | |
| # return all_regions | |
| all_regions_coords = [corners[regions] for regions in all_regions] | |
| return all_regions_coords | |
| def get_outwall(all_regions, corners, corner_sorted): | |
| """ | |
| Find the outermost boundary loop, which should be discarded | |
| """ | |
| if corner_sorted: | |
| regions_for_top_bot = np.nonzero([(0 in region and len(corners) - 1 in region) for region in all_regions])[0] | |
| if len(regions_for_top_bot) == 1: | |
| return regions_for_top_bot[0] | |
| else: | |
| areas = [_compute_region_area(corners[all_regions[idx]]) for idx in range(len(all_regions))] | |
| max_idx = np.argmax(areas) | |
| return max_idx | |
| else: | |
| areas = [_compute_region_area(corners[all_regions[idx]]) for idx in range(len(all_regions))] | |
| max_idx = np.argmax(areas) | |
| return max_idx | |
| # def filter_regions(all_regions): | |
| # areas = [_compute_region_area(corners[all_regions[idx]]) for idx in range(len(all_regions))] | |
| # all_regions = [region for idx, region in enumerate(all_regions) if areas[idx] > 20] | |
| # return all_regions | |
| def _compute_region_area(region): | |
| edge_map = np.zeros([256, 256]) | |
| for idx, c in enumerate(region[:-1]): | |
| cv2.line(edge_map, tuple(c), tuple(region[idx + 1]), 1, 3) | |
| reverse_edge_map = 1 - edge_map | |
| label, num_features = ndimage.label(reverse_edge_map) | |
| if num_features < 2: | |
| return 0 | |
| # import pdb; pdb.set_trace() | |
| # raise ValueError('Invalid region structure') | |
| bg_label = label[0, 0] | |
| num_labels = [(label == l).sum() for l in range(1, num_features + 1)] | |
| num_labels[bg_label - 1] = 0 | |
| room_label = np.argmax(num_labels) + 1 | |
| area = (label == room_label).sum() | |
| return area | |
| def _get_regions_for_corner(cur_idx, adj_mat, nb_orders): | |
| regions = list() | |
| if adj_mat[cur_idx].sum() == 0: | |
| assert ValueError("Zero-degree corner, should not reach here") | |
| # elif adj_mat[cur_idx].sum() == 1: # remove the connection if only one neighbour | |
| # other_idx = nb_orders[0] | |
| # import pdb; pdb.set_trace() | |
| # adj_mat[cur_idx, other_idx] = 0 | |
| else: | |
| v_s = cur_idx | |
| know_v_q = False | |
| while v_s is not None: | |
| if not know_v_q: | |
| v_p, v_q = _find_wedge_nbs(v_s, nb_orders, adj_mat) | |
| if v_p is None: # cannot find proper wedge, remove this corner | |
| adj_mat[v_s, :] = 0 | |
| adj_mat[:, v_s] = 0 | |
| break | |
| else: | |
| assert v_q is not None, "v_q should be known here" | |
| v_p = _find_wedge_third_v(v_q, v_s, nb_orders, adj_mat, dir=-1) | |
| if v_p is None: | |
| adj_mat[v_s, :] = 0 | |
| adj_mat[:, v_s] = 0 | |
| break | |
| cur_region = [ | |
| v_p, | |
| v_s, | |
| ] | |
| # try: | |
| assert adj_mat[v_p, v_s] == 1, "Wrong connection matrix!" | |
| # except: | |
| # import pdb; pdb.set_trace() | |
| adj_mat[v_p, v_s] = 0 | |
| region_i = 0 | |
| closed_polygon = False | |
| while v_q is not None: # tracking the current region | |
| cur_region.append(v_q) | |
| assert adj_mat[v_s, v_q] == 1, "Wrong connection matrix!" | |
| adj_mat[v_s, v_q] = 0 | |
| # update the nb order list for the current v_s | |
| if v_q == cur_region[0]: # get a closed polygon | |
| closed_polygon = True | |
| break | |
| else: | |
| v_p = cur_region[region_i + 1] | |
| v_s = cur_region[region_i + 2] | |
| v_q = _find_wedge_third_v(v_p, v_s, nb_orders, adj_mat, dir=1) | |
| if v_q is None: | |
| closed_polygon = False | |
| break | |
| region_i += 1 | |
| if closed_polygon: # find a closed region, keep iteration | |
| regions.append(cur_region) | |
| found_next = False | |
| for temp_i in range(1, len(cur_region)): | |
| if adj_mat[cur_region[temp_i], cur_region[temp_i - 1]] == 1: | |
| found_next = True | |
| v_s_idx = temp_i | |
| break | |
| if not found_next: | |
| v_s = None | |
| else: | |
| v_s = cur_region[v_s_idx] | |
| v_q = cur_region[v_s_idx - 1] | |
| know_v_q = True | |
| else: # no closed region, directly quit the search for the current v_s | |
| break | |
| return regions | |
| def _find_wedge_nbs(v_s, nb_orders, adj_mat): | |
| sorted_nbs = nb_orders[v_s] | |
| start_idx = 0 | |
| while True: | |
| if start_idx == -len(sorted_nbs): | |
| return None, None | |
| v_p, v_q = sorted_nbs[start_idx], sorted_nbs[start_idx - 1] | |
| if adj_mat[v_p, v_s] == 1 and adj_mat[v_s, v_q] == 1: | |
| return v_p, v_q | |
| else: | |
| start_idx -= 1 | |
| def _find_wedge_third_v(v1, v2, nb_orders, adj_mat, dir): | |
| sorted_nbs = nb_orders[v2] | |
| v1_idx = sorted_nbs.index(v1) | |
| if dir == 1: | |
| v3_idx = v1_idx - 1 | |
| while adj_mat[v2, sorted_nbs[v3_idx]] == 0: | |
| if sorted_nbs[v3_idx] == v1: | |
| return None | |
| v3_idx -= 1 | |
| elif dir == -1: | |
| v3_idx = v1_idx + 1 if v1_idx <= len(sorted_nbs) - 2 else 0 | |
| while adj_mat[sorted_nbs[v3_idx], v2] == 0: | |
| if sorted_nbs[v3_idx] == v1: | |
| return None | |
| v3_idx = v3_idx + 1 if v3_idx <= len(sorted_nbs) - 2 else 0 | |
| else: | |
| raise ValueError("unknown dir {}".format(dir)) | |
| return sorted_nbs[v3_idx] | |
| def _get_new_start(adj_mat, cur_idx, corners): | |
| for i in range(cur_idx, len(corners)): | |
| if adj_mat[i].sum() > 0: | |
| return i | |
| return None | |
| def _sort_neighours(adj_mat, corners): | |
| nb_orders = dict() | |
| for idx, c in enumerate(corners): | |
| nb_ids = np.nonzero(adj_mat[idx])[0] | |
| nb_degrees = [_compute_degree(c, corners[other_idx]) for other_idx in nb_ids] | |
| degree_ranks = np.argsort(nb_degrees) | |
| sort_nb_ids = [nb_ids[i] for i in degree_ranks] | |
| nb_orders[idx] = sort_nb_ids | |
| return nb_orders | |
| def _compute_degree(c1, c2): | |
| vec = (c2[0] - c1[0], -(c2[1] - c1[1])) # note that the y direction should be flipped (image coord system) | |
| cos = (vec[0] * 1 + vec[1] * 0) / np.sqrt(vec[0] ** 2 + vec[1] ** 2) | |
| theta = np.arccos(cos) | |
| if vec[1] < 0: | |
| theta = np.pi * 2 - theta | |
| return theta | |
| def preprocess_pg(pg): | |
| corners = pg["corners"] | |
| edge_pairs = pg["edges"] | |
| adj_mat = np.zeros([len(corners), len(corners)]) | |
| for edge_pair in edge_pairs: | |
| c1, c2 = edge_pair | |
| adj_mat[c1][c2] = 1 | |
| adj_mat[c2][c1] = 1 | |
| return corners, adj_mat | |
| def cleanup_pg(pg): | |
| corners = pg["corners"] | |
| edge_pairs = pg["edges"] | |
| adj_list = [[] for _ in range(len(corners))] | |
| for edge_pair in edge_pairs: | |
| adj_list[edge_pair[0]].append(edge_pair[1]) | |
| adj_list[edge_pair[1]].append(edge_pair[0]) | |
| for idx in range(len(corners)): | |
| if len(adj_list[idx]) < 2: | |
| _remove_corner(idx, adj_list) | |
| new_corners = list() | |
| removed_ids = list() | |
| old_to_new = dict() | |
| counter = 0 | |
| for c_i in range(len(adj_list)): | |
| if len(adj_list[c_i]) > 0: | |
| assert len(adj_list[c_i]) >= 2 | |
| new_corners.append(corners[c_i]) | |
| old_to_new[c_i] = counter | |
| counter += 1 | |
| else: | |
| removed_ids.append(c_i) | |
| new_edges = list() | |
| for c_i_1 in range(len(adj_list)): | |
| for c_i_2 in adj_list[c_i_1]: | |
| if c_i_1 < c_i_2: | |
| new_edge = (old_to_new[c_i_1], old_to_new[c_i_2]) | |
| new_edges.append(new_edge) | |
| new_corners = np.array(new_corners) | |
| new_edges = np.array(new_edges) | |
| new_pg = { | |
| "corners": new_corners, | |
| "edges": new_edges, | |
| } | |
| return new_pg | |
| def _remove_corner(idx, adj_list): | |
| assert len(adj_list[idx]) <= 1 | |
| if len(adj_list[idx]) == 0: | |
| return | |
| nbs = list(adj_list[idx]) | |
| adj_list[idx].pop(0) | |
| for nb in nbs: | |
| adj_list[nb].remove(idx) | |
| if len(adj_list[nb]) < 2: | |
| _remove_corner(nb, adj_list) | |
| def get_regions_from_pg(pg, corner_sorted): | |
| pg = cleanup_pg(pg) | |
| corners, adj_mat = preprocess_pg(pg) | |
| if len(corners) == 0: | |
| regions = [] | |
| else: | |
| regions = extract_regions(adj_mat, corners, corner_sorted) | |
| return regions | |
| def convert_annot(annot): | |
| corners = np.array(list(annot.keys())) | |
| corners_mapping = {tuple(c): idx for idx, c in enumerate(corners)} | |
| edges = set() | |
| for corner, connections in annot.items(): | |
| idx_c = corners_mapping[tuple(corner)] | |
| for other_c in connections: | |
| idx_other_c = corners_mapping[tuple(other_c)] | |
| if (idx_c, idx_other_c) not in edges and (idx_other_c, idx_c) not in edges: | |
| edges.add((idx_c, idx_other_c)) | |
| edges = np.array(list(edges)) | |
| pg_data = {"corners": corners, "edges": edges} | |
| return pg_data | |
| colors_12 = [ | |
| "#DCECC9", | |
| "#B3DDCC", | |
| "#8ACDCE", | |
| "#62BED2", | |
| "#46AACE", | |
| "#3D91BE", | |
| "#3677AE", | |
| "#2D5E9E", | |
| "#24448E", | |
| "#1C2B7F", | |
| "#162165", | |
| "#11174B", | |
| ] | |
| def plot_floorplan_with_regions(regions, corners, edges, scale): | |
| colors = colors_12[:8] | |
| regions = [(region * scale / 256).round().astype(np.int) for region in regions] | |
| corners = (corners * scale / 256).round().astype(np.int) | |
| # define the color map | |
| room_colors = [colors[i % 8] for i in range(len(regions))] | |
| colorMap = [tuple(int(h[i : i + 2], 16) for i in (1, 3, 5)) for h in room_colors] | |
| colorMap = np.asarray(colorMap) | |
| if len(regions) > 0: | |
| colorMap = np.concatenate([np.full(shape=(1, 3), fill_value=0), colorMap], axis=0).astype(np.uint8) | |
| else: | |
| colorMap = np.concatenate([np.full(shape=(1, 3), fill_value=0)], axis=0).astype(np.uint8) | |
| # when using opencv, we need to flip, from RGB to BGR | |
| colorMap = colorMap[:, ::-1] | |
| alpha_channels = np.zeros(colorMap.shape[0], dtype=np.uint8) | |
| alpha_channels[1 : len(regions) + 1] = 150 | |
| colorMap = np.concatenate([colorMap, np.expand_dims(alpha_channels, axis=-1)], axis=-1) | |
| room_map = np.zeros([scale, scale]).astype(np.int32) | |
| # sort regions | |
| if len(regions) > 1: | |
| avg_corner = [region.mean(axis=0) for region in regions] | |
| ind = np.argsort(np.array(avg_corner)[:, 0], axis=0) | |
| regions = np.array(regions)[ind] | |
| for idx, polygon in enumerate(regions): | |
| cv2.fillPoly(room_map, [polygon], color=idx + 1) | |
| image = colorMap[room_map.reshape(-1)].reshape((scale, scale, 4)) | |
| pointColor = tuple((np.array([0.95, 0.3, 0.3, 1]) * 255).astype(np.uint8).tolist()) | |
| for point in corners: | |
| cv2.circle(image, tuple(point), color=pointColor, radius=12, thickness=-1) | |
| cv2.circle(image, tuple(point), color=(255, 255, 255, 255), radius=6, thickness=-1) | |
| for edge in edges: | |
| c1 = corners[edge[0]] | |
| c2 = corners[edge[1]] | |
| cv2.line(image, tuple(c1), tuple(c2), color=(0, 0, 0, 255), thickness=3) | |
| return image | |