| import numpy as np |
| import torch |
|
|
| from shapely.geometry.polygon import LinearRing |
|
|
| try: |
| from pycolmap import image_to_world, world_to_image |
| except: |
| pass |
|
|
| |
|
|
| |
| def warp_points(points, homography): |
| |
| new_points = np.concatenate([points[..., [1, 0]], |
| np.ones_like(points[..., :1])], axis=-1) |
| |
| new_points = (homography @ new_points.T).T |
| |
| new_points = new_points[..., [1, 0]] / new_points[..., 2:] |
| return new_points |
|
|
|
|
| |
| def mask_points(points, img_size): |
| mask = ((points[..., 0] >= 0) |
| & (points[..., 0] < img_size[0]) |
| & (points[..., 1] >= 0) |
| & (points[..., 1] < img_size[1])) |
| return mask |
|
|
|
|
| |
| |
| def keypoints_to_grid(keypoints, img_size): |
| n_points = keypoints.size()[-2] |
| device = keypoints.device |
| grid_points = keypoints.float() * 2. / torch.tensor( |
| img_size, dtype=torch.float, device=device) - 1. |
| grid_points = grid_points[..., [1, 0]].view(-1, n_points, 1, 2) |
| return grid_points |
|
|
|
|
| |
| |
| def get_dist_mask(kp0, kp1, valid_mask, dist_thresh): |
| b_size, n_points, _ = kp0.size() |
| dist_mask0 = torch.norm(kp0.unsqueeze(2) - kp0.unsqueeze(1), dim=-1) |
| dist_mask1 = torch.norm(kp1.unsqueeze(2) - kp1.unsqueeze(1), dim=-1) |
| dist_mask = torch.min(dist_mask0, dist_mask1) |
| dist_mask = dist_mask <= dist_thresh |
| dist_mask = dist_mask.repeat(1, 1, b_size).reshape(b_size * n_points, |
| b_size * n_points) |
| dist_mask = dist_mask[valid_mask, :][:, valid_mask] |
| return dist_mask |
|
|
|
|
| |
|
|
| |
| def sample_line_points(lines, n): |
| line_points_x = np.linspace(lines[:, 0, 0], lines[:, 1, 0], n, axis=-1) |
| line_points_y = np.linspace(lines[:, 0, 1], lines[:, 1, 1], n, axis=-1) |
| line_points = np.stack([line_points_x, line_points_y], axis=2) |
| return line_points |
|
|
|
|
| |
| def mask_lines(lines, valid_mask): |
| h, w = valid_mask.shape |
| int_lines = np.clip(np.round(lines).astype(int), 0, [h - 1, w - 1]) |
| h_valid = valid_mask[int_lines[:, 0, 0], int_lines[:, 0, 1]] |
| w_valid = valid_mask[int_lines[:, 1, 0], int_lines[:, 1, 1]] |
| valid = h_valid & w_valid |
| return valid |
|
|
|
|
| |
| |
| def get_common_line_mask(line_indices, valid_mask): |
| b_size, n_points = line_indices.shape |
| common_mask = line_indices[:, :, None] == line_indices[:, None, :] |
| common_mask = common_mask.repeat(1, 1, b_size).reshape(b_size * n_points, |
| b_size * n_points) |
| common_mask = common_mask[valid_mask, :][:, valid_mask] |
| return common_mask |
|
|
|
|
| |
| def get_sAP_line_distance(warped_ref_line_seg, target_line_seg): |
| dist = (((warped_ref_line_seg[:, None, :, None] |
| - target_line_seg[:, None]) ** 2).sum(-1)) ** 0.5 |
| dist = np.minimum( |
| dist[:, :, 0, 0] + dist[:, :, 1, 1], |
| dist[:, :, 0, 1] + dist[:, :, 1, 0] |
| ) |
| return dist |
|
|
|
|
| |
| |
| |
| |
| def project_point_to_line(line_segs, points): |
| |
| dir_vec = (line_segs[:, 1] - line_segs[:, 0])[:, None] |
| coords1d = (((points[None] - line_segs[:, None, 0]) * dir_vec).sum(axis=2) |
| / np.linalg.norm(dir_vec, axis=2) ** 2) |
| |
| |
| |
| projection = line_segs[:, None, 0] + coords1d[:, :, None] * dir_vec |
| dist_to_line = np.linalg.norm(projection - points[None], axis=2) |
|
|
| return coords1d, dist_to_line |
|
|
|
|
| |
| |
| def get_segment_overlap(seg_coord1d): |
| seg_coord1d = np.sort(seg_coord1d, axis=-1) |
| overlap = ((seg_coord1d[..., 1] > 0) * (seg_coord1d[..., 0] < 1) |
| * (np.minimum(seg_coord1d[..., 1], 1) |
| - np.maximum(seg_coord1d[..., 0], 0))) |
| return overlap |
|
|
|
|
| |
| |
| |
| |
| def get_overlap_orth_line_dist(line_seg1, line_seg2, min_overlap=0.5): |
| n_lines1, n_lines2 = len(line_seg1), len(line_seg2) |
|
|
| |
| coords_2_on_1, line_dists2 = project_point_to_line( |
| line_seg1, line_seg2.reshape(n_lines2 * 2, -1)) |
| line_dists2 = line_dists2.reshape(n_lines1, n_lines2, 2).sum(axis=2) |
| coords_1_on_2, line_dists1 = project_point_to_line( |
| line_seg2, line_seg1.reshape(n_lines1 * 2, -1)) |
| line_dists1 = line_dists1.reshape(n_lines2, n_lines1, 2).sum(axis=2) |
| line_dists = (line_dists2 + line_dists1.T) / 2 |
|
|
| |
| coords_2_on_1 = coords_2_on_1.reshape(n_lines1, n_lines2, 2) |
| overlaps1 = get_segment_overlap(coords_2_on_1) |
| coords_1_on_2 = coords_1_on_2.reshape(n_lines2, n_lines1, 2) |
| overlaps2 = get_segment_overlap(coords_1_on_2).T |
| overlaps = (overlaps1 + overlaps2) / 2 |
|
|
| |
| low_overlaps = overlaps < min_overlap |
| line_dists[low_overlaps] = np.amax(line_dists) |
| return line_dists |
|
|
|
|
| |
|
|
| |
| def qvec2rotmat(qvec): |
| return np.array([ |
| [1 - 2 * qvec[2]**2 - 2 * qvec[3]**2, |
| 2 * qvec[1] * qvec[2] - 2 * qvec[0] * qvec[3], |
| 2 * qvec[3] * qvec[1] + 2 * qvec[0] * qvec[2]], |
| [2 * qvec[1] * qvec[2] + 2 * qvec[0] * qvec[3], |
| 1 - 2 * qvec[1]**2 - 2 * qvec[3]**2, |
| 2 * qvec[2] * qvec[3] - 2 * qvec[0] * qvec[1]], |
| [2 * qvec[3] * qvec[1] - 2 * qvec[0] * qvec[2], |
| 2 * qvec[2] * qvec[3] + 2 * qvec[0] * qvec[1], |
| 1 - 2 * qvec[1]**2 - 2 * qvec[2]**2]]) |
|
|
|
|
| |
| def rotmat2qvec(R): |
| Rxx, Ryx, Rzx, Rxy, Ryy, Rzy, Rxz, Ryz, Rzz = R.flat |
| K = np.array([ |
| [Rxx - Ryy - Rzz, 0, 0, 0], |
| [Ryx + Rxy, Ryy - Rxx - Rzz, 0, 0], |
| [Rzx + Rxz, Rzy + Ryz, Rzz - Rxx - Ryy, 0], |
| [Ryz - Rzy, Rzx - Rxz, Rxy - Ryx, Rxx + Ryy + Rzz]]) / 3.0 |
| eigvals, eigvecs = np.linalg.eigh(K) |
| qvec = eigvecs[[3, 0, 1, 2], np.argmax(eigvals)] |
| if qvec[0] < 0: |
| qvec *= -1 |
| return qvec |
|
|
|
|
| |
| def read_cameras(camera_file, scale_factor=None): |
| with open(camera_file, 'r') as f: |
| raw_cameras = f.read().rstrip().split('\n') |
| raw_cameras = raw_cameras[3:] |
| cameras = [] |
| for c in raw_cameras: |
| data = c.split(' ') |
| cameras.append({ |
| "model": data[1], |
| "width": int(data[2]), |
| "height": int(data[3]), |
| "params": np.array(list(map(float, data[4:])))}) |
|
|
| |
| if scale_factor is not None: |
| cameras = [scale_intrinsics(c, scale_factor) for c in cameras] |
| return cameras |
|
|
|
|
| |
| def scale_intrinsics(intrinsics, scale_factor): |
| new_intrinsics = {"model": intrinsics["model"], |
| "width": int(intrinsics["width"] * scale_factor + 0.5), |
| "height": int(intrinsics["height"] * scale_factor + 0.5) |
| } |
| params = intrinsics["params"] |
| |
| params[:2] *= scale_factor |
| |
| params[2:4] = (params[2:4] * scale_factor + 0.5) - 0.5 |
| new_intrinsics["params"] = params |
| return new_intrinsics |
|
|
|
|
| |
| def project_2d_to_3d(points, depth, T_local_to_world, intrinsics): |
| |
| world_points = image_to_world(points[:, [1, 0]], |
| intrinsics)['world_points'] |
| world_points *= depth[:, None] |
| world_points = np.concatenate([world_points, depth[:, None], |
| np.ones((len(depth), 1))], axis=1) |
|
|
| |
| world_points = (T_local_to_world @ world_points.T).T |
| world_points = world_points[:, :3] / world_points[:, 3:] |
| return world_points |
|
|
|
|
| |
| def project_3d_to_2d(points, T_world_to_local, intrinsics): |
| norm_points = np.concatenate([points, np.ones((len(points), 1))], axis=1) |
| norm_points = (T_world_to_local @ norm_points.T).T |
| norm_points = norm_points[:, :3] / norm_points[:, 3:] |
| norm_points = norm_points[:, :2] / norm_points[:, 2:] |
| image_points = world_to_image(norm_points, intrinsics) |
| image_points = np.stack(image_points['image_points'])[:, [1, 0]] |
| return image_points |
|
|
|
|
| |
|
|
| |
| |
| |
| def ellipse_polyline(ellipses, n=100): |
| t = np.linspace(0, 2*np.pi, n, endpoint=False) |
| st = np.sin(t) |
| ct = np.cos(t) |
| result = [] |
| for x0, y0, a, b, angle in ellipses: |
| angle = np.deg2rad(angle) |
| sa = np.sin(angle) |
| ca = np.cos(angle) |
| p = np.empty((n, 2)) |
| p[:, 0] = x0 + a * ca * ct - b * sa * st |
| p[:, 1] = y0 + a * sa * ct + b * ca * st |
| result.append(p) |
| return result |
|
|
|
|
| |
| def intersect_line_ellipse(a, line): |
| ea = LinearRing(a) |
| mp = ea.intersection(line) |
| if mp.is_empty: |
| return np.empty((0, 2)) |
| elif mp.geom_type == 'Point': |
| return np.array([[mp.x, mp.y]]) |
| elif mp.geom_type == 'MultiPoint': |
| return np.stack([[p.x for p in mp], [p.y for p in mp]], axis=-1) |
| else: |
| raise ValueError('Impossible geometry: ' + mp.geom_type) |