import cv2 import numpy as np from shapely import affinity from shapely.geometry import LineString, box def get_patch_coord(patch_box, patch_angle=0.0): patch_x, patch_y, patch_h, patch_w = patch_box x_min = patch_x - patch_w / 2.0 y_min = patch_y - patch_h / 2.0 x_max = patch_x + patch_w / 2.0 y_max = patch_y + patch_h / 2.0 patch = box(x_min, y_min, x_max, y_max) patch = affinity.rotate( patch, patch_angle, origin=(patch_x, patch_y), use_radians=False ) return patch def get_discrete_degree(vec, angle_class=36): deg = np.mod(np.degrees(np.arctan2(vec[1], vec[0])), 360) deg = (int(deg / (360 / angle_class) + 0.5) % angle_class) + 1 return deg def mask_for_lines(lines, mask, thickness, idx, type="index", angle_class=36): # draw mask for rasterization of a single line coords = np.asarray(list(lines.coords), np.int32) coords = coords.reshape((-1, 2)) if len(coords) < 2: return mask, idx if type == "backward": coords = np.flip(coords, 0) if type == "index": cv2.polylines(mask, [coords], False, color=idx, thickness=thickness) idx += 1 else: for i in range(len(coords) - 1): cv2.polylines( mask, [coords[i:]], False, color=get_discrete_degree( coords[i + 1] - coords[i], angle_class=angle_class ), thickness=thickness, ) return mask, idx def line_geom_to_mask( layer_geom, confidence_levels, local_box, canvas_size, thickness, idx, type="index", angle_class=36, ): patch_x, patch_y, patch_h, patch_w = local_box patch = get_patch_coord(local_box) canvas_h = canvas_size[0] canvas_w = canvas_size[1] scale_height = canvas_h / patch_h scale_width = canvas_w / patch_w trans_x = -patch_x + patch_w / 2.0 trans_y = -patch_y + patch_h / 2.0 map_mask = np.zeros(canvas_size, np.uint8) # loop through different line instances for a class for line in layer_geom: if isinstance(line, tuple): line, confidence = line else: confidence = None new_line = line.intersection(patch) if not new_line.is_empty: new_line = affinity.affine_transform( new_line, [1.0, 0.0, 0.0, 1.0, trans_x, trans_y] ) new_line = affinity.scale( new_line, xfact=scale_width, yfact=scale_height, origin=(0, 0) ) confidence_levels.append(confidence) # draw line and add idx by 1 every time, use it as color for instances if new_line.geom_type == "MultiLineString": for new_single_line in new_line: map_mask, idx = mask_for_lines( new_single_line, map_mask, thickness, idx, type, angle_class ) else: map_mask, idx = mask_for_lines( new_line, map_mask, thickness, idx, type, angle_class ) return map_mask, idx def overlap_filter(mask, filter_mask): C, _, _ = mask.shape for c in range(C - 1, -1, -1): filter = np.repeat((filter_mask[c] != 0)[None, :], c, axis=0) mask[:c][filter] = 0 return mask def preprocess_map( vectors, patch_size, canvas_size, num_classes, thickness, angle_class ): confidence_levels = [-1] vector_num_list = {} for i in range(num_classes): vector_num_list[i] = [] # cluster the lines based on their classes, easier to retrieve for vector in vectors: if vector["pts_num"] >= 2: vector_num_list[vector["type"]].append( LineString(vector["pts"][: vector["pts_num"]]) ) # print(vector_num_list) local_box = (0.0, 0.0, patch_size[0], patch_size[1]) idx = 1 filter_masks = [] instance_masks = [] forward_masks = [] backward_masks = [] # go through different classes of lines for i in range(num_classes): # print('\n') # print(idx) map_mask, idx = line_geom_to_mask( vector_num_list[i], confidence_levels, local_box, canvas_size, thickness, idx, ) # print(map_mask.shape) # print(np.unique(map_mask)) instance_masks.append(map_mask) # print(idx) filter_mask, _ = line_geom_to_mask( vector_num_list[i], confidence_levels, local_box, canvas_size, thickness + 4, 1, ) filter_masks.append(filter_mask) forward_mask, _ = line_geom_to_mask( vector_num_list[i], confidence_levels, local_box, canvas_size, thickness, 1, type="forward", angle_class=angle_class, ) forward_masks.append(forward_mask) backward_mask, _ = line_geom_to_mask( vector_num_list[i], confidence_levels, local_box, canvas_size, thickness, 1, type="backward", angle_class=angle_class, ) backward_masks.append(backward_mask) # zxc filter_masks = np.stack(filter_masks) instance_masks = np.stack(instance_masks) forward_masks = np.stack(forward_masks) backward_masks = np.stack(backward_masks) # filter overlapping area, some lane divider / road divider / ped crossing will be gone # print(len(instance_masks)) instance_masks = overlap_filter(instance_masks, filter_masks) forward_masks = overlap_filter(forward_masks, filter_masks).sum(0).astype("int32") backward_masks = overlap_filter(backward_masks, filter_masks).sum(0).astype("int32") # 2 x H x W # print(len(instance_masks)) # zxc semantic_masks = instance_masks != 0 return semantic_masks, instance_masks, forward_masks, backward_masks def rasterize_map(vectors, patch_size, canvas_size, num_classes, thickness): confidence_levels = [-1] vector_num_list = {} for i in range(num_classes): vector_num_list[i] = [] for vector in vectors: if vector["pts_num"] >= 2: vector_num_list[vector["type"]].append( ( LineString(vector["pts"][: vector["pts_num"]]), vector.get("confidence_level", 1), ) ) local_box = (0.0, 0.0, patch_size[0], patch_size[1]) idx = 1 masks = [] for i in range(num_classes): map_mask, idx = line_geom_to_mask( vector_num_list[i], confidence_levels, local_box, canvas_size, thickness, idx, ) masks.append(map_mask) return np.stack(masks), confidence_levels