def build_view_mat( common_obj: np.ndarray, common_img: np.ndarray, common_ids: np.ndarray, intrinsic_mat: np.ndarray ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, float]: _, rvec, tvec, inliers = cv2.solvePnPRansac( common_obj, common_img, intrinsic_mat, None, #flags=cv2.SOLVEPNP_EPNP, reprojectionError=RANSAC_REPROJECTION_ERROR, iterationsCount=100) mat = rodrigues_and_translation_to_view_mat3x4(rvec, tvec) inliers = np.array(inliers).reshape(-1) inlier_points = common_obj[inliers] inlier_img = common_img[inliers] inlier_ids = common_ids[inliers] reprojection_errors = compute_reprojection_errors(inlier_points, inlier_img, intrinsic_mat @ mat) #to_take = reprojection_errors < RANSAC_REPROJECTION_ERROR #reprojection_error = compute_reprojection_errors(inlier_points, inlier_img, intrinsic_mat @ mat).mean() #reprojection_error = np.linalg.norm(common_img - projections, axis=-1).mean() #return mat, inlier_points[to_take], inlier_ids[to_take], reprojection_errors[to_take].mean() return mat, inlier_points, inlier_ids, reprojection_errors.mean()
def pnp(self, cloud: PointCloudBuilder, camera): ids1, ids2 = build_index_intersection(cloud.ids, self.corners.ids) try: ret, rvec, tvec, inliers = cv2.solvePnPRansac( cloud.points[ids1], self.corners.points[ids2], camera, None, reprojectionError=MAX_REPROJECTION_ERROR) except: return self.last_inliners if not ret or len(inliers) < FrameTrack.MIN_INLIERS or np.linalg.norm( tvec) > MAX_TRANSLATION: return self.last_inliners potential_new_mtx = rodrigues_and_translation_to_view_mat3x4( rvec, tvec) potential_reprojection_error = compute_reprojection_errors( cloud.points[ids1], self.corners.points[ids2], camera @ potential_new_mtx) if self.current_reproject_error is None or potential_reprojection_error.mean( ) < self.current_reproject_error: self.mtx = potential_new_mtx self.current_reproject_error = potential_reprojection_error.mean() self.last_inliners = cloud.ids[ids1][np.array(inliers)] return self.last_inliners
def try_solve_pnp(self, points_3d: np.ndarray, points_2d: np.ndarray) -> Optional[Tuple[np.ndarray, np.ndarray]]: if len(points_3d) < 6: return None ok, r_vec, t_vec, inliers = cv2.solvePnPRansac( objectPoints=points_3d, imagePoints=points_2d, cameraMatrix=self.intrinsic_mat, distCoeffs=None, confidence=PNP_RANSAC_CONFIDENCE, reprojectionError=PNP_RANSAC_INLIERS_MAX_REPROJECTION_ERROR, flags=cv2.SOLVEPNP_EPNP ) if not ok: return None inliers = inliers.flatten() if len(inliers) < 6: return None _, r_vec, t_vec = cv2.solvePnP( points_3d, points_2d, self.intrinsic_mat, distCoeffs=None, rvec=r_vec, tvec=t_vec, useExtrinsicGuess=True, flags=cv2.SOLVEPNP_ITERATIVE ) return rodrigues_and_translation_to_view_mat3x4(r_vec, t_vec), inliers
def calc_vec_errors(vec, point2d): r, t = vec[0:3], vec[3:6] view_mat, point3d = rodrigues_and_translation_to_view_mat3x4( r.reshape(3, 1), t.reshape(3, 1)), vec[6:9] return compute_reprojection_errors(point3d.reshape(1, -1), point2d.reshape(1, -1), intrinsic_mat @ view_mat)[0]
def solve_pnp_ransac_one(self, frame_id): ids_3d = self.point_cloud_builder.ids ids_2d = self.corner_storage[frame_id].ids ids_intersection, _ = snp.intersect(ids_3d.flatten(), ids_2d.flatten(), indices=True) idx3d = [i for i, j in enumerate(ids_3d) if j in ids_intersection] idx2d = [i for i, j in enumerate(ids_2d) if j in ids_intersection] points3d = self.point_cloud_builder.points[idx3d] points2d = self.corner_storage[frame_id].points[idx2d] if len(points3d) < 6: return None, [] _, _, _, inliers = cv2.solvePnPRansac( objectPoints=points3d, imagePoints=points2d, cameraMatrix=self.intrinsic_mat, distCoeffs=np.array([]), iterationsCount=250, flags=cv2.SOLVEPNP_EPNP ) if len(points3d[inliers]) < 6: return None, [] _, r_vec, t_vec = cv2.solvePnP( objectPoints=points3d[inliers], imagePoints=points2d[inliers], cameraMatrix=self.intrinsic_mat, distCoeffs=np.array([]), flags=cv2.SOLVEPNP_ITERATIVE ) view_mat = rodrigues_and_translation_to_view_mat3x4(r_vec, t_vec) return view_mat, inliers
def _get_pos(self, frame_number): corners = self.corner_storage[frame_number] cur_corners, cur_cloud_pts = [], [] for i, corner in zip(corners.ids.flatten(), corners.points): if i in self.point_cloud.keys(): cur_corners.append(corner) cur_cloud_pts.append(self.point_cloud[i].pos) cur_corners, cur_cloud_pts = np.array(cur_corners), np.array( cur_cloud_pts) if len(cur_cloud_pts) < 4: return None # Not enough points for ransac is_success, r_vec, t_vec, inliers = cv2.solvePnPRansac( cur_cloud_pts, cur_corners, self.intrinsic_mat, None, flags=cv2.SOLVEPNP_EPNP) if not is_success: return None _, r_vec, t_vec = cv2.solvePnP(cur_cloud_pts[inliers], cur_corners[inliers], self.intrinsic_mat, distCoeffs=None, flags=cv2.SOLVEPNP_ITERATIVE, useExtrinsicGuess=True, rvec=r_vec, tvec=t_vec) pos = rodrigues_and_translation_to_view_mat3x4(r_vec, t_vec) inliers_n = len(np.array(inliers).flatten()) return CameraInfo(pos, inliers_n)
def camera_pose(id, corner_storage, point_cloud_builder, intrinsic_mat, dist_coef=None): frame_corners = corner_storage[id] points = frame_corners.points frame_ids = frame_corners.ids cloud_ids = point_cloud_builder.ids ids, frame_indexes, cloud_indexes = np.intersect1d(frame_ids, cloud_ids, return_indices=True) if ids.shape[0] < 4: return None cloud_points = point_cloud_builder.points[cloud_indexes] frame_points = points[frame_indexes] retval, rvec, tvec, inliers = cv2.solvePnPRansac(cloud_points, frame_points, intrinsic_mat, dist_coef) if retval is None: return None retval, rvec, tvec = cv2.solvePnP(cloud_points[inliers], frame_points[inliers], intrinsic_mat, dist_coef) if retval is None: return None return view_mat3x4_to_pose( rodrigues_and_translation_to_view_mat3x4(rvec, tvec)), np.setdiff1d( ids, inliers)
def _get_pos(self, frame_number) -> np.ndarray: corners = self.corner_storage[frame_number] _, pts_ids, corners_ids = np.intersect1d(self.pc_builder.ids, corners.ids, assume_unique=True, return_indices=True) common_pts_3d = self.pc_builder.points[pts_ids] common_ids = self.pc_builder.ids[pts_ids] common_corners = corners.points[corners_ids] inlier_mask = np.zeros_like(common_pts_3d, dtype=np.bool) inlier_counter = 0 for common_id, mask_elem in zip(common_ids[:, 0], inlier_mask): if common_id not in self.outliers: mask_elem[:] = True inlier_counter += 1 if inlier_counter > 7: common_pts_3d = common_pts_3d[inlier_mask].reshape(-1, 3) common_ids = common_ids[inlier_mask[:, :1]].reshape(-1, 1) common_corners = common_corners[inlier_mask[:, :2]].reshape(-1, 2) max_reproj_error = 3.0 if len(common_pts_3d) < 4: raise TrackingError('Not enough points to solve RANSAC on frame ' + str(frame_number)) _, rot_vec, tr_vec, inliers = cv2.solvePnPRansac(common_pts_3d, common_corners, self.intrinsic_mat, None, reprojectionError=max_reproj_error, flags=cv2.SOLVEPNP_EPNP) extrinsic_mat = rodrigues_and_translation_to_view_mat3x4(rot_vec, tr_vec) proj_matr = self.intrinsic_mat @ extrinsic_mat if inliers is None: raise TrackingError('Failed to solve PnP on frame' + str(frame_number)) while len(inliers) < 8 and max_reproj_error < 50: max_reproj_error *= 1.2 inliers = calc_inlier_indices(common_pts_3d, common_corners, proj_matr, max_reproj_error) inlier_pts = common_pts_3d[inliers] inlier_corners = common_corners[inliers] outlier_ids = np.setdiff1d(common_ids, common_ids[inliers], assume_unique=True) self.outliers.update(outlier_ids) if len(inliers) < 4: inlier_pts = common_pts_3d inlier_corners = common_corners print('Found position on', len(inlier_corners), 'inliers') _, rot_vec, tr_vec, inliers = cv2.solvePnPRansac(inlier_pts, inlier_corners, self.intrinsic_mat, None, rot_vec, tr_vec, useExtrinsicGuess=True) return rodrigues_and_translation_to_view_mat3x4(rot_vec, tr_vec)
def f(rtp): r_vec = rtp[:3].reshape(3, 1) t_vec = rtp[3:6].reshape(3, 1) p = rtp[6:] proj = intrinsic_mat @ rodrigues_and_translation_to_view_mat3x4( r_vec, t_vec) @ p proj /= proj[2] return np.square(proj - point2d).sum()
def apply_res(res_): for i in range(len(intersected)): p_ = res_[6 * len(frames) + i * 3:6 * len(frames) + i * 3 + 3] _idx = np.argwhere(self.point_cloud_builder.ids == intersected[i])[0][0] _p = self.point_cloud_builder.points[_idx] check_point(_p, p_, intersected[i]) for i in range(len(frames)): r_vec_ = np.array([res_[i * 6], res_[i * 6 + 1], res_[i * 6 + 2]]) t_vec_ = np.array([[res_[i * 6 + 3]], [res_[i * 6 + 4]], [res_[i * 6 + 5]]]) v_mat = rodrigues_and_translation_to_view_mat3x4(r_vec_, t_vec_) self.view_mats[frames[i]] = v_mat
def solve_pnp(points2d: np.ndarray, points3d: np.ndarray, intrinsic_mat: np.ndarray) -> np.ndarray: _, rvev, tvec = cv2.solvePnP( objectPoints=points3d.astype(np.float64), imagePoints=points2d, cameraMatrix=intrinsic_mat, distCoeffs=None ) return rodrigues_and_translation_to_view_mat3x4(rvev, tvec)
def solve_frame(frame_num, corner_storage, point_cloud_builder, view_mat, intrinsic_mat, direction): print("Frame ", frame_num) corners = corner_storage[frame_num] _, corner_indexes, points_indexes = np.intersect1d(corners.ids, point_cloud_builder.ids, assume_unique=True, return_indices=True) corners = corners.points[corner_indexes] points = point_cloud_builder.points[points_indexes] x, r, t, inliers = cv2.solvePnPRansac(points, corners, intrinsic_mat, None, reprojectionError=1.0, flags=cv2.SOLVEPNP_EPNP) good_corners = corners[inliers] good_points = points[inliers] x, r, t = cv2.solvePnP(good_points, good_corners, intrinsic_mat, None, r, t, useExtrinsicGuess=True, flags=cv2.SOLVEPNP_ITERATIVE) print("Points in cloud ", len(point_cloud_builder.ids)) new_views = rodrigues_and_translation_to_view_mat3x4(r, t) if new_views is None: view_mat[frame_num] = view_mat[frame_num + direction] else: view_mat[frame_num] = new_views for i in range(40): other_frame_num = frame_num + i * direction if check_baseline(view_mat[other_frame_num], view_mat[frame_num], 0.1): correspondences = build_correspondences( corner_storage[other_frame_num], corner_storage[frame_num], ids_to_remove=point_cloud_builder.ids) if len(correspondences) != 0: new_points, ids, _ = triangulate_correspondences( correspondences, view_mat[other_frame_num], view_mat[frame_num], intrinsic_mat, TriangulationParameters(1.0, 1.0, 0.1)) point_cloud_builder.add_points(ids, new_points)
def get_ransac(point_cloud_builder, corners_i, intrinsic_mat): intersection, (indexes_cloud, indexes_corners) = snp.intersect( point_cloud_builder.ids.flatten(), corners_i.ids.flatten(), indices=True) if len(intersection) < 6: return False, None, None, None try: res_code, rvec, tvec, inliers = cv2.solvePnPRansac( point_cloud_builder.points[indexes_cloud], corners_i.points[indexes_corners], intrinsic_mat, distCoeffs=None, reprojectionError=1.5) except Exception: print('Exception happened in solving PnP, continuing') return False, None, None, None rodrig = None cloud_points = None if res_code: rodrig = rodrigues_and_translation_to_view_mat3x4(rvec, tvec) cloud_points = point_cloud_builder.points[indexes_cloud][ inliers.flatten()] return res_code, rodrig, inliers, cloud_points
def track(self): for cur_index in range(self._length): print(f'Processing frame {cur_index}/{self._length}') corners = self._corner_storage[cur_index] ids = [] object_points = [] image_points = [] for id, point in zip(corners.ids, corners.points): indices_x, _ = np.where(self._builder.ids == id) if len(indices_x) == 0: continue ids.append(id) object_points.append(self._builder.points[indices_x[0]]) image_points.append(point) if len(object_points) < 5: continue retval, rvec, tvec, inliers = cv2.solvePnPRansac( np.array(object_points), np.array(image_points), self._intrinsic_mat, None, flags=cv2.SOLVEPNP_EPNP) if not retval: continue view_mat = rodrigues_and_translation_to_view_mat3x4(rvec, tvec) print(f'Position based on {len(inliers)} inliers') self._track[cur_index] = view_mat updatesCount = sum([self._update_cloud(cur_index, next_index) for next_index in range(cur_index) \ if self._track[next_index] is not None]) print( f'Points updated:{updatesCount} Current cloud size:{len(self._builder.ids)}' ) for i in range(len(self._track)): self._track[i] = self._track[i] if self._track[ i] is not None else self._track[i - 1] return np.array(self._track), self._builder
def track_and_calc_colors(camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None) \ -> Tuple[List[Pose], PointCloud]: if known_view_1 is None or known_view_2 is None: raise NotImplementedError() rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) intrinsic_mat = to_opencv_camera_mat3x3(camera_parameters, rgb_sequence[0].shape[0]) view_mats_tmp = [None for _ in range(len(corner_storage))] all_points = {} not_none_mats = [known_view_1[0], known_view_2[0]] none_mats = set( [ind for ind in range(len(view_mats_tmp)) if ind not in not_none_mats]) view_mats_tmp[known_view_1[0]] = pose_to_view_mat3x4(known_view_1[1]) view_mats_tmp[known_view_2[0]] = pose_to_view_mat3x4(known_view_2[1]) add_new_points(corner_storage, view_mats_tmp, known_view_1[0], known_view_2[0], all_points, intrinsic_mat) old_len = 2 for _ in range(len(view_mats_tmp)): for ind in none_mats: corner_ids = list(corner_storage[ind].ids.reshape(-1)) intersection_inds = [ point_ind for point_ind in corner_ids if point_ind in all_points and all_points[point_ind] is not None ] # There is an assert, so points number has to be at least 5 if len(intersection_inds) < 5: continue # print(ind, len(corner_storage), len(corner_storage[ind].points), np.max(intersection_inds)) corner_points = { i: p for i, p in zip(corner_ids, corner_storage[ind].points) } intersection_points_c = np.array( [corner_points[i] for i in intersection_inds]) intersection_points_f = np.array( [all_points[i] for i in intersection_inds]) # print(intersection_points_f) # print(intersection_points_f.shape, intersection_points_c.shape, intrinsic_mat) retval, rvec, tvec, inliers = cv2.solvePnPRansac( intersection_points_f, intersection_points_c, intrinsic_mat, None) if not retval: continue print(f'Processing frame: {ind}') newly_none_number = 0 for i in intersection_inds: if i not in inliers: newly_none_number += 1 all_points[i] = None print( f'{newly_none_number} points filled as None, len of inliers: {inliers.shape[0]}' ) print( f'Number of not points: {len([p for p in all_points.keys() if all_points[p] is not None])}' ) view_mats_tmp[ind] = rodrigues_and_translation_to_view_mat3x4( rvec, tvec) for not_none_ind in not_none_mats: add_new_points(corner_storage, view_mats_tmp, ind, not_none_ind, all_points, intrinsic_mat) not_none_mats.append(ind) none_mats.remove(ind) break if len(not_none_mats) == old_len: break old_len = len(not_none_mats) view_mats = [None for _ in range(len(corner_storage))] for view_mat_ind in not_none_mats: view_mats[view_mat_ind] = view_mats_tmp[view_mat_ind] last_ind = 0 for i in range(len(view_mats)): if view_mats[i] is None: view_mats[i] = view_mats[last_ind] else: last_ind = i all_points = {k: v for k, v in all_points.items() if v is not None} point_cloud_builder = PointCloudBuilder( np.array(list(all_points.keys()), dtype=np.int), np.array(list(all_points.values()))) calc_point_cloud_colors(point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud
def track_and_find_point_cloud(intrinsic_mat, corner_storage, known_view_1, known_view_2): num_frames = len(corner_storage) view_mats = [None] * num_frames point_cloud_builder = PointCloudBuilder() print(f'Frames with known views: {known_view_1[0]} and {known_view_2[0]}') view_mats[known_view_1[0]] = pose_to_view_mat3x4(known_view_1[1]) view_mats[known_view_2[0]] = pose_to_view_mat3x4(known_view_2[1]) triang_params = TriangulationParameters(max_reprojection_error=MAX_REPR_ERR, min_triangulation_angle_deg=MIN_TRIANG_ANGLE_DEG, min_depth=MIN_DEPTH) add_points_to_cloud(point_cloud_builder, corner_storage[known_view_1[0]], corner_storage[known_view_2[0]], view_mats[known_view_1[0]], view_mats[known_view_2[0]], intrinsic_mat, triang_params) while True: was_updated = False for i in range(num_frames): if view_mats[i] is not None: continue print(f"\nCurrent frame: {i}") # find intersection of current point cloud and current frame corners = corner_storage[i] corner_ids = [] points_3d = [] points_2d = [] for id, corner in zip(corners.ids, corners.points): if id not in point_cloud_builder.ids: continue ind_in_builder, _ = np.nonzero(point_cloud_builder.ids == id) corner_ids.append(id) points_3d.append(point_cloud_builder.points[ind_in_builder[0]]) points_2d.append(corner) print(f"Size of intersection of current point cloud and current frame: {len(corner_ids)}") if len(corner_ids) < 5: continue points_3d = np.array(points_3d) points_2d = np.array(points_2d) retval, rvec, tvec, inliers = cv2.solvePnPRansac(points_3d.reshape((-1, 1, 3)), points_2d.reshape((-1, 1, 2)), cameraMatrix=intrinsic_mat, distCoeffs=None, reprojectionError=PNP_REPROJ_ERROR) if not retval: print("Unsuccessful solution of PnP") continue print(f"Position found by {len(inliers)} inliers") view_mat = rodrigues_and_translation_to_view_mat3x4(rvec, tvec) view_mats[i] = view_mat for j in range(num_frames): if i != j and view_mats[j] is not None: points_added = add_points_to_cloud(point_cloud_builder, corner_storage[i], corner_storage[j], view_mats[i], view_mats[j], intrinsic_mat, triang_params) if points_added > 0: was_updated = True print(f"Current size of point cloud: {point_cloud_builder.points.size}") if was_updated is False: break for i in range(num_frames): if view_mats[i] is None: view_mats[i] = view_mats[i - 1] return view_mats, point_cloud_builder
def bundle_adjustment(view_mats, point_cloud_builder, intrinsic_mat, corner_storage): ids2d, ids3d, frameids = [], [], [] for frame in range(len(view_mats)): _, (id1, id2) = snp.intersect(point_cloud_builder.ids.flatten(), corner_storage[frame].ids.flatten(), indices=True) inliers = calc_inlier_indices(point_cloud_builder.points[id1], corner_storage[frame].points[id2], intrinsic_mat @ view_mats[frame], 1.0) for i in inliers: ids2d.append(id2[i]) ids3d.append(id1[i]) frameids.append(frame) view_mats_vec = [] for view_mat in view_mats: tvec = view_mat[:, 3] rvec, _ = cv2.Rodrigues(view_mat[:, :3]) view_mats_vec.append(np.concatenate([rvec.squeeze(), tvec])) view_mats_vec = np.array(view_mats_vec) used_3ds = list(set(ids3d)) points3d_vec = np.array(point_cloud_builder.points[used_3ds]) for i in range(len(ids3d)): ids3d[i] = used_3ds.index(ids3d[i]) n, m = len(view_mats_vec.reshape(-1)), len(points3d_vec.reshape(-1)) def calc_vec_errors(vec, point2d): r, t = vec[0:3], vec[3:6] view_mat, point3d = rodrigues_and_translation_to_view_mat3x4( r.reshape(3, 1), t.reshape(3, 1)), vec[6:9] return compute_reprojection_errors(point3d.reshape(1, -1), point2d.reshape(1, -1), intrinsic_mat @ view_mat)[0] def calc_jacobian(): J = np.zeros((len(frameids), n + m)) for i in range(len(frameids)): frameid, id3d, id2d = frameids[i], ids3d[i], ids2d[i] point3d = points3d_vec[id3d] point2d = corner_storage[frameid].points[id2d] vec = np.concatenate([view_mats_vec[frameid], point3d]) der = scipy.optimize.approx_fprime( vec, lambda vec: calc_vec_errors(vec, point2d), np.full(vec.shape, 1e-7)) J[i, n + 3 * id3d:n + 3 * id3d + 3] = der[6:] J[i, 6 * frameid:6 * frameid + 6] = der[:6] return J def calc_errors(): errors = [] for i in range(len(frameids)): frameid, id3d, id2d = frameids[i], ids3d[i], ids2d[i] errors.append( calc_vec_errors( np.concatenate( [view_mats_vec[frameid], points3d_vec[id3d]]), corner_storage[frameid].points[id2d])) return errors error_before_bundle_adjustment = np.mean(calc_errors()) for iter in range(5): J = calc_jacobian() u = np.array(calc_errors()) g = J.T @ u gx, gc = g[n:], g[:n] Q = J.T @ J + 10 * np.diag(np.diag(J.T @ J)) U, W, V = Q[:n, :n], Q[:n, n:], Q[n:, n:] try: V_inv = np.linalg.inv(V) deltac = np.linalg.solve(U - W @ V_inv @ W.T, W @ V_inv @ gx - gc) deltax = -V_inv @ (gx + W.T @ deltac) view_mats_vec = (view_mats_vec.reshape(-1) + deltac).reshape( (-1, 6)) points3d_vec = (points3d_vec.reshape(-1) + deltax).reshape((-1, 3)) except: pass print('Bundle-adjustment: iteration {}, error {}'.format( iter, np.mean(calc_errors()))) sys.stdout.flush() error_after_bundle_adjustment = np.mean(calc_errors()) if error_after_bundle_adjustment > error_before_bundle_adjustment: print('Better without bundle adjustment') return view_mats point_cloud_builder.update_points(point_cloud_builder.ids[used_3ds], points3d_vec) new_view_mats = [] for vec in view_mats_vec: rvec, tvec = vec[0:3], vec[3:6] view_mat = rodrigues_and_translation_to_view_mat3x4( rvec.reshape(3, 1), tvec.reshape(3, 1)) new_view_mats.append(view_mat) return np.array(new_view_mats)
def track_and_calc_colors( camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None ) -> Tuple[List[Pose], PointCloud]: rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) intrinsic_mat = to_opencv_camera_mat3x3(camera_parameters, rgb_sequence[0].shape[0]) if known_view_1 is None or known_view_2 is None: known_view_1, known_view_2 = calculate_known_views( intrinsic_mat, corner_storage) print('Calculated known views: {} and {}'.format( known_view_1[0], known_view_2[0])) random.seed(239) view_mats = np.full((len(rgb_sequence), ), None) view_mats[known_view_1[0]] = pose_to_view_mat3x4(known_view_1[1]) view_mats[known_view_2[0]] = pose_to_view_mat3x4(known_view_2[1]) point_cloud_builder = PointCloudBuilder() def triangulate_and_add_points(corners1, corners2, view_mat1, view_mat2): points3d, ids, median_cos = triangulate_correspondences( build_correspondences(corners1, corners2), view_mat1, view_mat2, intrinsic_mat, triang_params) point_cloud_builder.add_points(ids, points3d) triangulate_and_add_points(corner_storage[known_view_1[0]], corner_storage[known_view_2[0]], view_mats[known_view_1[0]], view_mats[known_view_2[0]]) inliers_points3d, inliers_points2d = None, None def find_errs(rtvecs): rvecs, tvecs = rtvecs[:3], rtvecs[3:] rmat, _ = cv2.Rodrigues(np.expand_dims(rvecs, axis=1)) rmat = np.concatenate((rmat, np.expand_dims(tvecs, axis=1)), axis=1) return (project_points(inliers_points3d, intrinsic_mat @ rmat) - inliers_points2d).flatten() while True: random_ids = list(range(len(view_mats))) random.shuffle(random_ids) found_new_view_mat = False for i in random_ids: if view_mats[i] is not None: continue common, (ids1, ids2) = snp.intersect(point_cloud_builder.ids.flatten(), corner_storage[i].ids.flatten(), indices=True) if len(common) <= 10: continue points3d = point_cloud_builder.points[ids1] points2d = corner_storage[i].points[ids2] retval, rvecs, tvecs, inliers = cv2.solvePnPRansac( points3d, points2d, intrinsic_mat, iterationsCount=108, reprojectionError=triang_params.max_reprojection_error, distCoeffs=None, confidence=0.999, flags=cv2.SOLVEPNP_EPNP) if retval: # M-оценки inliers_points3d = points3d[inliers.flatten()] inliers_points2d = points2d[inliers.flatten()] rtvecs = least_squares(find_errs, x0=np.concatenate( (rvecs, tvecs)).flatten(), loss='huber', method='trf').x rvecs = rtvecs[:3].reshape((-1, 1)) tvecs = rtvecs[3:].reshape((-1, 1)) if not retval: continue print( 'Iteration {}/{}, processing {}th frame: {} inliers, {} points in point cloud' .format( len([v for v in view_mats if v is not None]) - 1, len(rgb_sequence) - 2, i, len(inliers), len(point_cloud_builder.points))) view_mats[i] = rodrigues_and_translation_to_view_mat3x4( rvecs, tvecs) found_new_view_mat = True # Add new points inliers = np.array(inliers).astype(int).flatten() inlier_corners = FrameCorners( *[c[inliers] for c in corner_storage[i]]) for j in range(len(view_mats)): if view_mats[j] is not None: triangulate_and_add_points(corner_storage[j], inlier_corners, view_mats[j], view_mats[i]) sys.stdout.flush() if not found_new_view_mat: break for i in range(0, len(view_mats)): if view_mats[i] is None: print('Я сдох: не все кадры обработаны :(') exit(1) if len(view_mats) < 100: # Иначе долго работает view_mats = bundle_adjustment(view_mats, point_cloud_builder, intrinsic_mat, corner_storage) calc_point_cloud_colors(point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud
def track_and_calc_colors(camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None) \ -> Tuple[List[Pose], PointCloud]: rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) frames_num = len(rgb_sequence) intrinsic_mat = to_opencv_camera_mat3x3(camera_parameters, rgb_sequence[0].shape[0]) if known_view_1 is None or known_view_2 is None: known_view_1, known_view_2 = select_frames(rgb_sequence, corner_storage, intrinsic_mat) if known_view_2[0] == -1: print("Failed to find good starting frames") exit(0) correspondences = build_correspondences(corner_storage[known_view_1[0]], corner_storage[known_view_2[0]]) view_mat1 = pose_to_view_mat3x4(known_view_1[1]) view_mat2 = pose_to_view_mat3x4(known_view_2[1]) points, ids, _ = triangulate_correspondences(correspondences, view_mat1, view_mat2, intrinsic_mat, TRIANG_PARAMS) if len(ids) < 8: print("Bad frames: too few correspondences!") exit(0) view_mats, point_cloud_builder = [None] * frames_num, PointCloudBuilder( ids, points) view_mats[known_view_1[0]] = view_mat1 view_mats[known_view_2[0]] = view_mat2 updated = True pass_num = 0 while updated: updated = False pass_num += 1 for i in range(frames_num): if view_mats[i] is not None: continue corners = corner_storage[i] _, ind1, ind2 = np.intersect1d(point_cloud_builder.ids, corners.ids.flatten(), return_indices=True) try: _, rvec, tvec, inliers = cv2.solvePnPRansac( point_cloud_builder.points[ind1], corners.points[ind2], intrinsic_mat, distCoeffs=None) if inliers is None: print(f"Pass {pass_num}, frame {i}. No inliers!") continue print( f"Pass {pass_num}, frame {i}. Number of inliers == {len(inliers)}" ) view_mats[i] = rodrigues_and_translation_to_view_mat3x4( rvec, tvec) except Exception: continue updated = True cloud_changed = add_points(point_cloud_builder, corner_storage, i, view_mats, intrinsic_mat) if cloud_changed: print(f"Size of point cloud == {len(point_cloud_builder.ids)}") first_not_none = next(item for item in view_mats if item is not None) if view_mats[0] is None: view_mats[0] = first_not_none for i in range(frames_num): if view_mats[i] is None: view_mats[i] = view_mats[i - 1] calc_point_cloud_colors(point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud
def run_bundle_adjustment(intrinsic_mat: np.ndarray, list_of_corners: List[FrameCorners], max_inlier_reprojection_error: float, view_mats: List[np.ndarray], pc_builder: PointCloudBuilder) -> List[np.ndarray]: n_frames = len(view_mats) inliers = get_inliers(intrinsic_mat, list_of_corners, max_inlier_reprojection_error, view_mats, pc_builder) rs = [cv2.Rodrigues(i[:, :3])[0].flatten() for i in view_mats] ts = [i[:, 3] for i in view_mats] point_id_to_cloud = -np.ones(pc_builder.ids.max() + 1, dtype=np.int32) point_id_to_cloud[pc_builder.ids.flatten()] = np.arange(len( pc_builder.ids)) re = calc_reprojection_error(inliers, pc_builder, point_id_to_cloud, list_of_corners, intrinsic_mat, view_mats) print('Reprojection error before bundle adjustment:', re) for start in range(0, n_frames, FRAMES_STEP): end = min(start + FRAMES_STEP, n_frames) cameras_params_dim = (end - start) * 6 matches = get_matches(inliers[start:end], len(list_of_corners), start) relevant_point_ids = np.array( list( sorted( {point_id for point_id, frame_id, point_index in matches}))) point_id_to_cur = {p: i for i, p in enumerate(relevant_point_ids)} _, (_, cloud_indices) = snp.intersect(relevant_point_ids, pc_builder.ids.flatten(), indices=True) relevant_points = pc_builder.points[cloud_indices] alpha = ALPHA_INITIAL us_mean_prev = -float('inf') for i in range(ITERATIONS): us = get_residuals(pc_builder, point_id_to_cloud, list_of_corners, intrinsic_mat, matches, view_mats) jacobian = calc_jacobian(matches, cameras_params_dim, rs, ts, pc_builder, list_of_corners, point_id_to_cur, point_id_to_cloud, intrinsic_mat, start) jtj = jacobian.T @ jacobian us_mean = us.mean() alpha = alpha / ALPHA_DEC_STEP if us_mean < us_mean_prev else alpha * ALPHA_INC_STEP us_mean_prev = us_mean alpha_multiplier = 1 + alpha jtj[np.arange(jtj.shape[0]), np.arange(jtj.shape[0])] *= alpha_multiplier u = jtj[:cameras_params_dim, :cameras_params_dim] v = jtj[cameras_params_dim:, cameras_params_dim:] w = jtj[:cameras_params_dim, cameras_params_dim:] wt = jtj[cameras_params_dim:, :cameras_params_dim] v_inv = block_inv(v) g = jacobian.T @ us b = w @ v_inv @ g[cameras_params_dim:] - g[:cameras_params_dim] a = (u - w @ v_inv @ wt).toarray() delta_c = np.linalg.solve(a, b) delta_x = v_inv @ (-g[cameras_params_dim:] - wt @ delta_c) for k, j in enumerate(range(start, end)): rs[j] += delta_c[k * 6:k * 6 + 3] ts[j] += delta_c[k * 6 + 3:k * 6 + 6] view_mats[j] = rodrigues_and_translation_to_view_mat3x4( rs[j], ts[j].reshape(3, 1)) relevant_points += delta_x.reshape((-1, 3)) pc_builder.update_points(relevant_point_ids, relevant_points) re = calc_reprojection_error(inliers, pc_builder, point_id_to_cloud, list_of_corners, intrinsic_mat, view_mats) print('Reprojection error after bundle adjustment:', re) return view_mats
def track_and_calc_colors(camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None) \ -> Tuple[List[Pose], PointCloud]: # функция для проверки что матрица поворота дейтсвительно матрица поворота def check_if_mat_is_rot_mat(mat): mat = mat[:, :3] det = np.linalg.det(mat) # определитель должен быть = 1 diff1 = abs(1 - det) x = mat @ mat.T # обратная матрица должна получаться транспонированием diff2 = abs(np.sum(np.eye(3) - x)) eps = 0.001 return diff1 < eps and diff2 < eps MAX_REPROJECTION_ERROR = 1.6 MIN_DIST_TRIANGULATE = 0.01 rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) intrinsic_mat = to_opencv_camera_mat3x3(camera_parameters, rgb_sequence[0].shape[0]) frame_count = len(corner_storage) t_vecs = [None] * frame_count view_mats = [None] * frame_count # позиции камеры на всех кадрах # для каждого уголка находим все кадры на котором он есть и его порядковый номер в каждом из них cur_corners_occurencies = {} frames_of_corner = {} for i, corners in enumerate(corner_storage): for id_in_list, j in enumerate(corners.ids.flatten()): cur_corners_occurencies[j] = 0 if j not in frames_of_corner.keys(): frames_of_corner[j] = [[i, id_in_list]] else: frames_of_corner[j].append([i, id_in_list]) #по двум кадрам находим общие точки и восстанавливаем их позиции в 3D def triangulate(frame_0, frame_1, params=TriangulationParameters(2, 1e-3, 1e-4), ids_to_remove=None) \ -> Tuple[np.ndarray, np.ndarray, float]: corrs = build_correspondences(corner_storage[frame_0], corner_storage[frame_1]) return triangulate_correspondences(corrs, view_mats[frame_0], view_mats[frame_1], intrinsic_mat, params) #константы для инициализации BASELINE_THRESHOLD = 0.9 REPROJECTION_ERROR_THRESHOLD = 1.4 MIN_3D_POINTS = 10 frame_steps = [9, 30, 40] #нахождение относительного движения между двумя данными кадрами def get_first_cam_pose(frame_0, frame_1): corrs = build_correspondences(corner_storage[frame_0], corner_storage[frame_1]) e_mat, _ = cv2.findEssentialMat(corrs.points_1, corrs.points_2, intrinsic_mat, cv2.RANSAC, 0.999, 2.0) def essential_mat_to_camera_view_matrix(e_mat): U, S, VH = np.linalg.svd(e_mat, full_matrices=True) W = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]], dtype=np.float) u3 = U[:, 2] possible_R = [U @ W @ VH, U @ W.T @ VH] possible_t = [u3, -u3] best_R = None best_t = None max_positive_z = 0 for R in possible_R: for t in possible_t: view_mat0 = np.hstack((np.eye(3), np.zeros((3, 1)))) view_mat1 = np.hstack((R, t.reshape((3, 1)))) view_mats[frame_0] = view_mat0 view_mats[frame_1] = view_mat1 points3d, ids, median_cos = triangulate(frame_0, frame_1) cur_positive_z = 0 for pt in points3d: pt = np.append(pt, np.zeros((1))).reshape((4, 1)) pt_transformed0 = (view_mat0 @ pt).flatten() z0 = pt_transformed0[2] pt_transformed1 = (view_mat1 @ pt).flatten() z1 = pt_transformed1[2] cur_positive_z += (z0 > 0.1) + (z1 > 0.1) if cur_positive_z > max_positive_z: max_positive_z = cur_positive_z best_R = R best_t = t return best_R, best_t R, t = essential_mat_to_camera_view_matrix(e_mat) baseline = np.linalg.norm(t) view_mat0 = np.hstack((np.eye(3), np.zeros((3, 1)))) view_mat1 = np.hstack((R, t.reshape((3, 1)))) view_mats[frame_0] = view_mat0 view_mats[frame_1] = view_mat1 points3d, ids, median_cos = triangulate(frame_0, frame_1) points0 = [] points1 = [] for i in ids: for j in frames_of_corner[i]: if j[0] == frame_0: points0.append(j[1]) if j[0] == frame_1: points1.append(j[1]) points0 = np.array(points0) points1 = np.array(points1) sum_error = compute_reprojection_errors( points3d, corner_storage[frame_0].points[points0], intrinsic_mat @ view_mat0) + compute_reprojection_errors( points3d, corner_storage[frame_1].points[points1], intrinsic_mat @ view_mat1) reprojection_error = np.mean(sum_error) points3d_count = len(points3d) view_mats[frame_0] = None view_mats[frame_1] = None #проверяем насколько хороша данная пара кадров, и заодно проверяем что матрица R - реально поворот if baseline < BASELINE_THRESHOLD or reprojection_error > REPROJECTION_ERROR_THRESHOLD or points3d_count < MIN_3D_POINTS or not check_if_mat_is_rot_mat( np.hstack((R, t.reshape((3, 1))))): return False, baseline, reprojection_error, points3d_count, None, None else: return True, baseline, reprojection_error, points3d_count, R, t #делаем перебор пар кадров для инициализации INF = 1e9 best_frame_step = 5 best = [-INF, INF, -INF] ind = None for frame_step in frame_steps: for i in range(frame_count // 2): if i + frame_step < frame_count * 0.85: ok, baseline, reproection_error, points3d_count, Rx, tx = get_first_cam_pose( i, i + frame_step) if ok: best = [baseline, reproection_error, points3d_count] ind = i best_frame_step = frame_step if frame_count > 100: break # крашаемся, если не удалось инициализироваться ни по какой паре кадров if ind is None: raise NotImplementedError() #инициализируемся по лучшей найденной паре frame_0 = ind frame_1 = ind + best_frame_step ok, baseline, reproection_error, points3d_count, R, t = get_first_cam_pose( frame_0, frame_1) print('INITIALIZATION RESULTS:') print('FRAMES: frame_0={}, frame_1={}, total_frames={}'.format( frame_0, frame_1, frame_count)) print(baseline, reproection_error, points3d_count, R, t) view_mat0 = np.hstack((np.eye(3), np.zeros((3, 1)))) view_mat1 = np.hstack((R, t.reshape((3, 1)))) view_mats[frame_0] = view_mat0 view_mats[frame_1] = view_mat1 t_vecs[frame_0] = np.zeros((3), dtype=np.float64) t_vecs[frame_1] = t # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # зачем хранить инфу о пендинг точках вперемешку с остальными точками облака??? # 'id': coords [., ., .] 3d coordinates, rays [(frame, coords)] - for new points addition points_cloud = {} # инициализируем облако точек по 2 положениям points3d, ids, median_cos = triangulate(frame_0, frame_1) for id, point3d in zip(ids, points3d): points_cloud[id] = point3d def find_camera_pose(frame_id): # сначала находим инлаеры ранзаком # потом на инлаерах делаем пнп с итеративной мин кв corners = corner_storage[frame_id] points3d = [] points2d = [] for id, point2d in zip(corners.ids.flatten(), corners.points): if id in points_cloud.keys(): points3d.append(points_cloud[id]) points2d.append(point2d) # чтобы епнп работал points3d = np.array(points3d, dtype=np.float64) points2d = np.array(points2d, dtype=np.float64) if len(points3d) < 4: return None # выбираем эталонную 4 точек, строим по ней R и t # затем смотрим, на какой выборке эталонных точек больше всего других точек # спроецируются корректно success, R, t, inliers = cv2.solvePnPRansac( objectPoints=points3d, imagePoints=points2d, cameraMatrix=intrinsic_mat, distCoeffs=None, flags=cv2.SOLVEPNP_EPNP, confidence=0.9995, reprojectionError=MAX_REPROJECTION_ERROR) if not success: return None inliers = np.asarray(inliers, dtype=np.int32).flatten() points3d = np.array(points3d) points2d = np.array(points2d) points3d = points3d[inliers] points2d = points2d[inliers] retval, R, t = cv2.solvePnP(objectPoints=points3d, imagePoints=points2d, cameraMatrix=intrinsic_mat, distCoeffs=None, flags=cv2.SOLVEPNP_ITERATIVE, useExtrinsicGuess=True, rvec=R, tvec=t) return R, t, len(inliers) # можно ускорить def frames_without_pose(): frames = [] for ind, mat in enumerate(view_mats): if mat is None: frames.append(ind) return frames def try_add_new_point_to_cloud(corner_id): # все фреймы на которых есть данный уголок, и у которых уже найдена view_mat # а можно для уголка хранить не все фреймы где он есть, а только те, где он инлаер! frames = [] for frame in frames_of_corner[corner_id]: if view_mats[frame[0]] is not None: frames.append(frame) if len(frames) < 2: return max_dist = 0 best_frames = [None, None] best_ids = [None, None] # можно ускорить хотя бы вдвое for frame_1 in frames: for frame_2 in frames: if frame_1 == frame_2: continue # эм, по идее это не возможно, когда есть view_mat есть и t_vec if t_vecs[frame_1[0]] is None or t_vecs[frame_2[0]] is None: continue t_vec_1 = t_vecs[frame_1[0]] t_vec_2 = t_vecs[frame_2[0]] dist = np.linalg.norm(t_vec_1 - t_vec_2) if max_dist < dist: max_dist = dist best_frames = [frame_1[0], frame_2[0]] best_ids = [frame_1[1], frame_2[1]] if max_dist > MIN_DIST_TRIANGULATE: ids = np.array([corner_id]) points1 = corner_storage[best_frames[0]].points[np.array( [best_ids[0]])] points2 = corner_storage[best_frames[1]].points[np.array( [best_ids[1]])] # что за ужасный синтаксис?????? corrs = corrs = Correspondences(ids, points1, points2) points3d, ids, med_cos = triangulate_correspondences( corrs, view_mats[best_frames[0]], view_mats[best_frames[1]], intrinsic_mat, parameters=TriangulationParameters(2, 1e-3, 1e-4)) # зачем проверка??? зачем делаем поп????? if len(points3d) > 0: points_cloud[ids[0]] = points3d[0] cur_corners_occurencies.pop(ids[0], None) # в этой функции мы еще бонусом возвращаем те точки, которые встречаются хотя бы 2 раза # а также мы добавляем оккуранси тем точкам которые уже в облаке)))))))))))))))))))))))))))) def add_corners_occurencies(frame_id): two_and_greater = [] for id in corner_storage[frame_id].ids.flatten(): if id in cur_corners_occurencies.keys(): cur_corners_occurencies[id] += 1 if cur_corners_occurencies[id] >= 2: two_and_greater.append(id) return two_and_greater # очень эффективно)))))))))) while len(frames_without_pose()) > 0: # пока есть не найденные положения камеры будем искать лучшее для восстановления # т.е. такое, где больше всего инлаеров после применения пнп ранзака wanted_frames = frames_without_pose() # inliers_amount = [] не понял зачем этот лист max_col = -1 best_frame_id = -1 best_R = None best_t = None for frame_id in wanted_frames: result = find_camera_pose(frame_id) if result is not None: R, t, col = result if col > max_col: max_col = col best_frame_id = frame_id best_R = R best_t = t if max_col == -1: # больше позиции камер не находятся break view_mats[best_frame_id] = rodrigues_and_translation_to_view_mat3x4( best_R, best_t) t_vecs[best_frame_id] = best_t try_add = add_corners_occurencies(best_frame_id) for corner in try_add: try_add_new_point_to_cloud(corner) print( 'Now we add camera pose on {}-th frame, camera pose was calculated by {} inliers' .format(best_frame_id, max_col)) ttl_poses_calculated = np.sum([mat is not None for mat in view_mats]) percent = "{:.0f}".format(ttl_poses_calculated / frame_count * 100.0) print('{}% poses calculated'.format(percent)) print('{} points in 3D points cloud'.format(len(points_cloud))) last_view_mat = None for i in range(frame_count): if view_mats[i] is None: view_mats[i] = last_view_mat else: last_view_mat = view_mats[i] #check that all matrices are rotation matrices for ind, i in enumerate(view_mats): if not check_if_mat_is_rot_mat(i): print( 'In frame {}/{} we have matrix wich is not rotation matrix:('. format(ind + 1, frame_count)) raise NotImplementedError() ids = [] points = [] for id in points_cloud.keys(): ids.append(id) points.append(points_cloud[id]) point_cloud_builder = PointCloudBuilder(np.array(ids, dtype=np.int32), np.array(points)) calc_point_cloud_colors(point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud
def track_and_calc_colors(camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None) \ -> Tuple[List[Pose], PointCloud]: if known_view_1 is None or known_view_2 is None: raise NotImplementedError() rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) intrinsic_mat = to_opencv_camera_mat3x3(camera_parameters, rgb_sequence[0].shape[0]) triang_params = TriangulationParameters( max_reprojection_error=MAX_PROJ_ERROR, min_triangulation_angle_deg=MIN_TRIANG_ANG, min_depth=MIN_DEPTH) correspondences = build_correspondences(corner_storage[known_view_1[0]], corner_storage[known_view_2[0]]) view_mat_1 = pose_to_view_mat3x4(known_view_1[1]) view_mat_2 = pose_to_view_mat3x4(known_view_2[1]) points, ids, median_cos = triangulate_correspondences( correspondences, view_mat_1, view_mat_2, intrinsic_mat, triang_params) if len(points) < 10: print("\rPlease, try other frames") exit(0) view_mats = [None] * len(rgb_sequence) view_mats[known_view_1[0]] = view_mat_1 view_mats[known_view_2[0]] = view_mat_2 point_cloud_builder = PointCloudBuilder(ids, points) was_update = True while was_update: was_update = False for i, (frame, corners) in enumerate(zip(rgb_sequence, corner_storage)): if view_mats[i] is not None: continue _, (idx_1, idx_2) = snp.intersect(point_cloud_builder.ids.flatten(), corners.ids.flatten(), indices=True) try: retval, rvec, tvec, inliers = cv2.solvePnPRansac( point_cloud_builder.points[idx_1], corners.points[idx_2], intrinsic_mat, distCoeffs=None) inliers = np.array(inliers, dtype=int) if len(inliers) > 0: view_mats[i] = rodrigues_and_translation_to_view_mat3x4( rvec, tvec) was_update = True print( f"\rFrame {i} out of {len(rgb_sequence)}, inliners: {len(inliers)}", end="") except Exception: print(f"\rFrame {i} out of {len(rgb_sequence)}, inliners: {0}", end="") if view_mats[i] is None: continue cur_corner = filter_frame_corners(corner_storage[i], inliers.flatten()) for j in range(len(rgb_sequence)): if view_mats[j] is None: continue correspondences = build_correspondences( corner_storage[j], cur_corner) if len(correspondences.ids) == 0: continue points, ids, median_cos = triangulate_correspondences( correspondences, view_mats[j], view_mats[i], intrinsic_mat, triang_params) point_cloud_builder.add_points(ids, points) print() first_mat = next((mat for mat in view_mats if mat is not None), None) if first_mat is None: print("\rFail") exit(0) view_mats[0] = first_mat for i in range(1, len(view_mats)): if view_mats[i] is None: view_mats[i] = view_mats[i - 1] view_mats = np.array(view_mats) calc_point_cloud_colors(point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud
def track_and_calc_colors(camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None) \ -> Tuple[List[Pose], PointCloud]: if known_view_1 is None or known_view_2 is None: raise NotImplementedError() rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) intrinsic_mat = to_opencv_camera_mat3x3(camera_parameters, rgb_sequence[0].shape[0]) view_mats = np.zeros((len(corner_storage), 3, 4), dtype=np.float64) processed_frames = np.zeros(len(corner_storage), dtype=np.bool) points3d = np.zeros((corner_storage.max_corner_id() + 1, 3), dtype=np.float64) added_points = np.zeros(corner_storage.max_corner_id() + 1, dtype=np.bool) print('Trying to extract 3d points from known frames...') view_mats[known_view_1[0]] = pose_to_view_mat3x4(known_view_1[1]) view_mats[known_view_2[0]] = pose_to_view_mat3x4(known_view_2[1]) processed_frames[known_view_1[0]] = processed_frames[ known_view_2[0]] = True extracted_points = extract_points(corner_storage[known_view_1[0]], corner_storage[known_view_2[0]], view_mats[known_view_1[0]], view_mats[known_view_2[0]], intrinsic_mat) if not extracted_points: print( 'Extracting 3d points from known frames failed: ' 'either there are no common points, or triangulation angle between frames is too small.\n' 'Try to choose another initial frames.', file=sys.stderr) exit(0) print('Succeeded! Trying to build point cloud...') added_points[extracted_points[0]] = True points3d[extracted_points[0]] = extracted_points[1] was_updated = True while was_updated: was_updated = False unprocessed_frames = np.arange(len(corner_storage), dtype=np.int32)[~processed_frames] for frame in unprocessed_frames: points3d_ids = np.arange(corner_storage.max_corner_id() + 1, dtype=np.int32)[added_points] common, (indices1, indices2) = snp.intersect( points3d_ids, corner_storage[frame].ids.flatten(), indices=True) if len(common) <= 5: continue try: print(f'Processing frame {frame}: ', end='') retval, rvec, tvec, inliers = cv2.solvePnPRansac( objectPoints=points3d[common], imagePoints=corner_storage[frame].points[indices2], cameraMatrix=intrinsic_mat, distCoeffs=None) except: retval = False if not retval: print(f'failed to solve PnP RANSAC, trying another frame') continue print(f'succeeded, found {len(inliers)} inliers') was_updated = True view_mats[frame] = rodrigues_and_translation_to_view_mat3x4( rvec, tvec) for processed_frame in np.arange(len(corner_storage), dtype=np.int32)[processed_frames]: extracted_points = extract_points( corner_storage[frame], corner_storage[processed_frame], view_mats[frame], view_mats[processed_frame], intrinsic_mat, points3d_ids) if extracted_points: added_points[extracted_points[0]] = True points3d[extracted_points[0]] = extracted_points[1] processed_frames[frame] = True print(f'Current point cloud contains {sum(added_points)} points') for _ in range(2): for i in range(1, len(corner_storage)): if not processed_frames[i]: processed_frames[i] = True view_mats[i] = view_mats[i - 1] processed_frames = processed_frames[::-1] view_mats = view_mats[::-1] print( f'Finished building point cloud, now it contains {sum(added_points)} points' ) point_cloud_builder = PointCloudBuilder(ids=np.arange( corner_storage.max_corner_id() + 1, dtype=np.int32)[added_points], points=points3d[added_points]) calc_point_cloud_colors(point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud
def track_and_calc_colors(camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None) \ -> Tuple[List[Pose], PointCloud]: if known_view_1 is None or known_view_2 is None: raise NotImplementedError() rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) intrinsic_mat = to_opencv_camera_mat3x3( camera_parameters, rgb_sequence[0].shape[0] ) # TODO: implement frame_count = len(corner_storage) view_mats = [None] * frame_count view_1_id, view_1 = known_view_1[0], pose_to_view_mat3x4(known_view_1[1]) view_2_id, view_2 = known_view_2[0], pose_to_view_mat3x4(known_view_2[1]) view_mats[view_1_id] = view_1 view_mats[view_2_id] = view_2 correspondences = build_correspondences(corner_storage[view_1_id], corner_storage[view_2_id], None) triangulation_parameters = TriangulationParameters(1, 5, 0) points, ids, _ = triangulate_correspondences(correspondences, view_1, view_2, intrinsic_mat, TriangulationParameters(0.1, 0.1, 0)) #corners_0 = corner_storage[0] point_cloud_builder = PointCloudBuilder(ids, points) unknown_frames = frame_count - 2 known_frames = {view_1_id, view_2_id} while unknown_frames > 0: for i in range(frame_count): if view_mats[i] is None: corners = corner_storage[i] interesting_ids, in_corners, in_cloud = np.intersect1d(corners.ids.flatten(), point_cloud_builder.ids.flatten(), return_indices=True) points_2d = corners.points[in_corners] points_3d = point_cloud_builder.points[in_cloud] if len(ids) < 3: continue method = cv2.SOLVEPNP_EPNP if len(ids) == 3: method = cv2.SOLVEPNP_P3P retval, rvec, tvec, inliers = cv2.solvePnPRansac(points_3d, points_2d, intrinsic_mat, None, flags=method) if not retval: continue retval, rvec, tvec = cv2.solvePnP(points_3d[inliers], points_2d[inliers], intrinsic_mat, None, rvec=rvec, tvec=tvec, useExtrinsicGuess=True) if not retval: continue view_mats[i] = rodrigues_and_translation_to_view_mat3x4(rvec, tvec) unknown_frames -= 1 known_frames.add(i) outliers = np.delete(interesting_ids, inliers) for frame in known_frames: correspondences = build_correspondences(corners, corner_storage[frame], outliers) points_3d, corner_ids, _ = triangulate_correspondences( correspondences, view_mats[i], view_mats[frame], intrinsic_mat, triangulation_parameters) point_cloud_builder.add_points(corner_ids, points_3d) calc_point_cloud_colors( point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0 ) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud
def track_and_calc_colors(camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None) \ -> Tuple[List[Pose], PointCloud]: rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) intrinsic_mat = to_opencv_camera_mat3x3(camera_parameters, rgb_sequence[0].shape[0]) frame_count = len(corner_storage) view_mats = [None] * frame_count if known_view_1 is None or known_view_2 is None: known_view_1, known_view_2 = init_first_camera_positions( intrinsic_mat, corner_storage) id_1, view_mat_1 = known_view_1 id_2, view_mat_2 = known_view_2 view_mats[id_1], view_mats[id_2] = view_mat_1, view_mat_2 else: id_1, pose_1 = known_view_1 id_2, pose_2 = known_view_2 view_mats[id_1] = pose_to_view_mat3x4(pose_1) view_mats[id_2] = pose_to_view_mat3x4(pose_2) params = TriangulationParameters(max_reprojection_error=2, min_triangulation_angle_deg=1, min_depth=0.5) correspondences = build_correspondences(corner_storage[id_1], corner_storage[id_2]) points, ids, _ = triangulate_correspondences(correspondences, view_mats[id_1], view_mats[id_2], intrinsic_mat, params) point_cloud_builder = PointCloudBuilder(ids.reshape(-1), points) while True: update = False for i in range(frame_count): if view_mats[i] is not None: continue corners = corner_storage[i] ids, ids1, ids2 = np.intersect1d(corners.ids, point_cloud_builder.ids, return_indices=True) points_2d, points_3d = corners.points[ ids1], point_cloud_builder.points[ids2] if len(ids) < 6: continue retval, rvec, tvec, inliers = solvePnPRansac(points_3d, points_2d, intrinsic_mat, distCoeffs=None) if not retval: continue retval, rvec, tvec = solvePnP(points_3d[inliers], points_2d[inliers], intrinsic_mat, distCoeffs=None, rvec=rvec, tvec=tvec, useExtrinsicGuess=True) if not retval: continue view_mats[i] = rodrigues_and_translation_to_view_mat3x4(rvec, tvec) update = True outliers = np.delete(ids, inliers) for j in range(i): if view_mats[j] is None: continue correspondences = build_correspondences(corners, corner_storage[j], ids_to_remove=outliers) points, ids, _ = triangulate_correspondences( correspondences, view_mats[i], view_mats[j], intrinsic_mat, params) point_cloud_builder.add_points(ids.reshape(-1), points) click.echo("Process frame %d/%d. %d 3D points found. inliners=%d" % (i + 1, frame_count, len( point_cloud_builder.points), len(inliers))) if not update: break calc_point_cloud_colors(point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud
def track_and_calc_colors(camera_parameters: CameraParameters, corner_storage: CornerStorage, frame_sequence_path: str, known_view_1: Optional[Tuple[int, Pose]] = None, known_view_2: Optional[Tuple[int, Pose]] = None) \ -> Tuple[List[Pose], PointCloud]: rgb_sequence = frameseq.read_rgb_f32(frame_sequence_path) intrinsic_mat = to_opencv_camera_mat3x3(camera_parameters, rgb_sequence[0].shape[0]) if known_view_1 is None or known_view_2 is None: known_view_1, known_view_2 = calc_known_views(corner_storage, intrinsic_mat) # TODO: implement num_frames_retr = 10 triang_params = TriangulationParameters(8.0, 1.0, .1) num_frames = len(corner_storage) frame_1 = known_view_1[0] frame_2 = known_view_2[0] print(f'{frame_1}, {frame_2}') tvecs = [None] * num_frames tvecs[frame_1] = known_view_1[1].t_vec tvecs[frame_2] = known_view_2[1].t_vec view_mat_1 = pose_to_view_mat3x4(known_view_1[1]) view_mat_2 = pose_to_view_mat3x4(known_view_2[1]) view_mats = [None] * num_frames view_mats[frame_1] = view_mat_1 view_mats[frame_2] = view_mat_2 cloud = {} corrs = build_correspondences(corner_storage[frame_1], corner_storage[frame_2]) points3d, ids, _ = triangulate_correspondences(corrs, view_mat_1, view_mat_2, intrinsic_mat, triang_params) for point3d, id in zip(points3d, ids): cloud[id] = point3d current_corners_occurences = {} corner_to_frames = {} for i, corners in enumerate(corner_storage): for j, id in enumerate(corners.ids.flatten()): current_corners_occurences[id] = 0 if id not in corner_to_frames.keys(): corner_to_frames[id] = [[i, j]] else: corner_to_frames[id].append([i, j]) while len(untracked_frames(view_mats)) > 0: untr_frames = untracked_frames(view_mats) max_num_inl = -1 best_frame = -1 best_rvec = None best_tvec = None for frame in untr_frames: rvec, tvec, num_inl = calc_camera_pose(frame, corner_storage, cloud, intrinsic_mat) if rvec is not None: if num_inl > max_num_inl: best_frame = frame max_num_inl = num_inl best_rvec = rvec best_tvec = tvec if max_num_inl == -1: break corners_to_add = [] for id in corner_storage[best_frame].ids.flatten(): if id in current_corners_occurences.keys(): current_corners_occurences[id] += 1 if current_corners_occurences[id] >= 2: corners_to_add.append(id) for corner in corners_to_add: add_new_point(corner, corner_to_frames, view_mats, tvecs, corner_storage, cloud, current_corners_occurences, intrinsic_mat) print(f"Frame: {best_frame}, Inliers: {max_num_inl}") print(f"Cloud size: {len(cloud)}") view_mats[best_frame] = rodrigues_and_translation_to_view_mat3x4( best_rvec, best_tvec) tvecs[best_frame] = best_tvec last_mat = None for frame in range(num_frames): if view_mats[frame] is None: view_mats[frame] = last_mat else: last_mat = view_mats[frame] ids = [] points = [] for id in cloud.keys(): ids.append(id) points.append(cloud[id]) point_cloud_builder = PointCloudBuilder(np.array(ids, dtype=np.int), np.array(points)) calc_point_cloud_colors(point_cloud_builder, rgb_sequence, view_mats, intrinsic_mat, corner_storage, 5.0) point_cloud = point_cloud_builder.build_point_cloud() poses = list(map(view_mat3x4_to_pose, view_mats)) return poses, point_cloud