Esempio n. 1
0
def camera(transform: Isometry = Isometry(),
           wh_ratio: float = 4.0 / 3.0,
           scale: float = 1.0,
           fovx: float = 90.0,
           color_id: int = -1):
    pw = np.tan(np.deg2rad(fovx / 2.)) * scale
    ph = pw / wh_ratio
    all_points = np.asarray([
        [0.0, 0.0, 0.0],
        [pw, ph, scale],
        [pw, -ph, scale],
        [-pw, ph, scale],
        [-pw, -ph, scale],
    ])
    line_indices = np.asarray([[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3],
                               [3, 4], [2, 4]])
    geom = o3d.geometry.LineSet(points=o3d.utility.Vector3dVector(all_points),
                                lines=o3d.utility.Vector2iVector(line_indices))

    if color_id == -1:
        my_color = np.zeros((3, ))
    else:
        my_color = np.asarray(
            matplotlib.cm.get_cmap('tab10').colors)[color_id, :3]
    geom.colors = o3d.utility.Vector3dVector(
        np.repeat(np.expand_dims(my_color, 0), line_indices.shape[0], 0))

    geom.transform(transform.matrix)
    return geom
Esempio n. 2
0
    def compute_sdf_Hg(self,
                       n_iter: int,
                       last_pose: Isometry,
                       cur_delta_pose: Isometry,
                       obs_xyz: torch.Tensor,
                       no_grad: bool = False):
        """
        Get the error function: L(xi) = mu_[T(xi)p] / std_[T(xi)p].detach()
        :param cur_pose:
        :param obs_xyz: (N, 3) in camera space.
        :return: f: (M, )  J: (M, 6)
        """
        cur_obs_xyz = (last_pose.dot(cur_delta_pose)) @ obs_xyz

        cur_obs_xyz.requires_grad_(not no_grad)
        cur_obs_sdf, cur_obs_std, cur_obs_valid_mask = self.map.get_sdf(
            cur_obs_xyz)
        # print(cur_obs_std.min(), cur_obs_std.max()) # ~0.13
        cur_obs_sdf = cur_obs_sdf / cur_obs_std.detach()

        if no_grad:
            JW = None
        else:
            dsdf_dpos = torch.autograd.grad(
                cur_obs_sdf, [cur_obs_xyz],
                grad_outputs=torch.ones_like(cur_obs_sdf),
                retain_graph=False,
                create_graph=False)[0]  # (N, 3)
            del cur_obs_xyz
            dsdf_dpos = dsdf_dpos[cur_obs_valid_mask]  # (M, 3)
            cur_obs_sdf = cur_obs_sdf.detach()
            cur_dxyz = (cur_delta_pose @ obs_xyz)[cur_obs_valid_mask]
            Lt = torch.from_numpy(
                last_pose.q.rotation_matrix.astype(np.float32).T).cuda()
            Lai = torch.mm(dsdf_dpos, Lt)
            Lbi = torch.cross(cur_dxyz, Lai)
            dsdf_dxi = torch.cat([Lai, Lbi], dim=-1)
            JW = dsdf_dxi

        Wf = cur_obs_sdf
        # print("[Empirical] Robust kernel should be = ", 2.6 * torch.std(Wf))
        if self.sdf_args.robust_kernel is not None:
            term_weight = self._robust_kernel(cur_obs_sdf,
                                              self.sdf_args.robust_kernel,
                                              self.sdf_args.robust_k)
            Wf = Wf * term_weight  # (M)
            JW = JW * term_weight.unsqueeze(1) if JW is not None else None

        error_scale = 1.0 / Wf.size(0)
        sum_error = (cur_obs_sdf * Wf).sum().item() * error_scale

        if no_grad:
            return None, None, float(sum_error)
        else:
            H = torch.einsum('na,nb->nab', JW, dsdf_dxi).sum(0) * error_scale
            g = (dsdf_dxi * Wf.unsqueeze(1)).sum(0) * error_scale
            return H.cpu().numpy().astype(float), g.cpu().numpy().astype(
                float), float(sum_error)
Esempio n. 3
0
def compute_flow(base_pc: np.ndarray, base_segms: np.ndarray, base_cam: Isometry, base_motions: list,
                 dest_cam: Isometry, dest_motions: list):
    n_parts = len(base_motions)
    final_pc = np.empty_like(base_pc)
    for part_id in range(n_parts):
        part_mask = np.where(base_segms == (part_id + 1))[0]
        part_pc = (dest_cam.inv().dot(dest_motions[part_id]).dot(
            base_motions[part_id].inv()).dot(base_cam)) @ base_pc[part_mask]
        final_pc[part_mask] = part_pc
    return final_pc - base_pc
Esempio n. 4
0
    def __getitem__(self, data_id):
        idx, view_sel_idx = data_id // len(self.view_sel), data_id % len(self.view_sel)
        pcs, segms, trans_dict = self._get_item(idx)
        n_parts = len(trans_dict) - 1
        n_views = pcs.shape[0]

        view_sel = self.view_sel[view_sel_idx]
        if view_sel is None:
            view_sel = list(range(n_views))

        def get_view_motions(view_id):
            return [Isometry.from_matrix(trans_dict[t][view_id]) for t in range(1, n_parts + 1)]

        ret_vals = {}

        if DatasetSpec.PC in self.spec:
            ret_vals[DatasetSpec.PC] = pcs[view_sel, ...]

        if DatasetSpec.SEGM in self.spec:
            ret_vals[DatasetSpec.SEGM] = segms[view_sel, ...]

        if DatasetSpec.FLOW in self.spec:
            assert len(view_sel) == 2
            view0_id, view1_id = view_sel
            flow12 = compute_flow(pcs[view0_id], segms[view0_id],
                                  Isometry.from_matrix(trans_dict['cam'][view0_id]), get_view_motions(view0_id),
                                  Isometry.from_matrix(trans_dict['cam'][view1_id]), get_view_motions(view1_id))
            ret_vals[DatasetSpec.FLOW] = flow12

        if DatasetSpec.FULL_FLOW in self.spec:
            all_flows = []
            for view_i in view_sel:
                for view_j in view_sel:
                    flow_ij = compute_flow(pcs[view_i], segms[view_i],
                                           Isometry.from_matrix(trans_dict['cam'][view_i]), get_view_motions(view_i),
                                           Isometry.from_matrix(trans_dict['cam'][view_j]), get_view_motions(view_j))
                    all_flows.append(flow_ij)
            all_flows = np.stack(all_flows)
            ret_vals[DatasetSpec.FULL_FLOW] = all_flows

        return [ret_vals[k] for k in self.spec]
Esempio n. 5
0
    def track_camera_points_lm(self, init_pose: Isometry,
                               obs_xyz: torch.Tensor):
        assert obs_xyz.size(1) == 3
        cur_pose = init_pose
        damping = self.args.lm_damping_init
        for i_iter in range(self.args.n_gn_iter):
            f, dsdf_dxi = self.get_error_func(cur_pose,
                                              obs_xyz,
                                              need_grad=True)  # (M, ), (M, 6)

            print(torch.std(f))

            term_weight = torch.ones_like(f)
            if self.args.robust_kernel:
                obs_sdf_abs = torch.abs(f)  # (M,)
                term_weight = torch.where(obs_sdf_abs <= self.args.robust_k,
                                          term_weight,
                                          self.args.robust_k / obs_sdf_abs)

            Wf = f * term_weight  # (M, 1)
            H = torch.einsum('nx,ny->xy', dsdf_dxi * term_weight.unsqueeze(1),
                             dsdf_dxi).cpu().numpy()  # (6, 6)
            lambda_DtD = damping * np.diag(np.diag(H))
            g = -(dsdf_dxi * Wf.unsqueeze(1)).sum(0).cpu().numpy()  # (6,)
            xi = np.linalg.solve(H + lambda_DtD, g)

            new_pose = Isometry.from_twist(xi) @ cur_pose
            rho_denom = (xi * (lambda_DtD @ xi)).sum() + (xi * g).sum()
            f_new = self.get_error_func(new_pose, obs_xyz,
                                        need_grad=False)  # (M, )

            new_weight = torch.ones_like(f_new)
            if self.args.robust_kernel:
                f_abs = torch.abs(f_new)
                new_weight = torch.where(f_abs <= self.args.robust_k,
                                         new_weight,
                                         self.args.robust_k / f_abs)

            rho = ((f * Wf.squeeze()).sum() -
                   (f_new * new_weight * f_new).sum()).item() / rho_denom

            if rho > self.args.lm_eps4:
                damping /= self.args.lm_ldown
                cur_pose = new_pose
                print(f"LM Iter {i_iter} LM Accepted: {(f ** 2).sum().item()}")
            else:
                damping *= self.args.lm_lup
                print(f"LM Iter {i_iter} LM Rejected: {(f ** 2).sum().item()}")
            damping = min(max(damping, 1.0e-7), 1.0e7)

        return cur_pose
Esempio n. 6
0
def frame(transform: Isometry = Isometry(), size=1.0):
    frame_obj = o3d.geometry.TriangleMesh.create_coordinate_frame(size=size)
    frame_obj.transform(transform.matrix)
    return frame_obj
Esempio n. 7
0
    def track_camera(self,
                     rgb_data: torch.Tensor,
                     depth_data: torch.Tensor,
                     calib: FrameIntrinsic,
                     set_pose: Isometry = None):
        """
        :param rgb_data:    (H, W, 3)       float32
        :param depth_data:  (H, W)          float32
        :param calib:       FrameIntrinsic
        :param set_pose:    Force set pose.
        :return: a pose.
        """
        cur_intensity = torch.mean(rgb_data, dim=-1)
        cur_depth = depth_data
        cur_intensity, cur_depth, cur_dIdxy = self._make_image_pyramid(
            cur_intensity, cur_depth)
        cur_rgb = rgb_data.permute(2, 0, 1)  # (3, H, W)

        # Process to point cloud.
        pc_scale = self.sdf_args.subsample
        pc_data = torch.nn.functional.interpolate(
            cur_depth[0].unsqueeze(0).unsqueeze(0),
            scale_factor=pc_scale,
            mode='nearest',
            recompute_scale_factor=False).squeeze(0).squeeze(0)
        cur_rgb = torch.nn.functional.interpolate(
            cur_rgb.unsqueeze(0),
            scale_factor=pc_scale,
            mode='bilinear',
            recompute_scale_factor=False).squeeze(0)
        pc_data = unproject_depth(pc_data, calib.fx * pc_scale,
                                  calib.fy * pc_scale, calib.cx * pc_scale,
                                  calib.cy * pc_scale)
        pc_data = torch.cat([
            pc_data,
            torch.zeros(
                (pc_data.size(0), pc_data.size(1), 1), device=pc_data.device)
        ],
                            dim=-1)
        pc_data = pc_data.reshape(-1, 4)
        cur_rgb = cur_rgb.permute(1, 2, 0)  # (W, H, 3)
        cur_rgb = cur_rgb.reshape(-1, 3)

        nan_mask = ~torch.isnan(pc_data[..., 0])
        pc_data = pc_data[nan_mask]
        cur_rgb = cur_rgb[nan_mask]

        with torch.cuda.device(self.map.device):
            pc_data_valid_mask = remove_radius_outlier(pc_data, 16, 0.05)
            pc_data = pc_data[pc_data_valid_mask]
            cur_rgb = cur_rgb[pc_data_valid_mask]
            normal_data = estimate_normals(pc_data, 16, 0.1, [0.0, 0.0, 0.0])
            normal_valid_mask = ~torch.isnan(normal_data[..., 0])
            normal_data = normal_data[normal_valid_mask]
            cur_rgb = cur_rgb[normal_valid_mask]
            pc_data = pc_data[normal_valid_mask, :3]

        self.last_colored_pcd = [pc_data, cur_rgb]
        pc_data, normal_data = point_box_filter(pc_data, normal_data, 0.02)
        self.last_processed_pc = [pc_data, normal_data]

        if set_pose is not None:
            final_pose = set_pose
        else:
            assert len(self.all_pd_pose) > 0
            lspeed = Isometry()
            final_pose = self.gauss_newton(self.all_pd_pose[-1].dot(lspeed),
                                           cur_intensity, cur_depth, cur_dIdxy,
                                           pc_data, calib)

        self.last_intensity = cur_intensity
        self.last_depth = cur_depth
        self.all_pd_pose.append(final_pose)
        return final_pose
Esempio n. 8
0
    def gauss_newton(self, init_pose: Isometry, cur_intensity_pyramid: list,
                     cur_depth_pyramid: list, cur_dIdxy_pyramid: list,
                     obs_xyz: torch.Tensor, calib: FrameIntrinsic):
        last_pose = self.all_pd_pose[-1]
        cur_delta_pose = last_pose.inv().dot(init_pose)
        last_delta_pose = copy.deepcopy(cur_delta_pose)

        i_iter = 0
        for group_iter_config in self.args.iter_config:
            last_energy = np.inf

            from utils.exp_util import AverageMeter
            loss_meter = AverageMeter()
            for i_iter in list(range(group_iter_config["n"])) + [-1]:

                H = np.zeros((6, 6), dtype=float)
                g = np.zeros((6, ), dtype=float)
                cur_energy = 0.0

                for loss_config in group_iter_config["type"]:
                    if loss_config[0] == 'sdf':
                        sdf_H, sdf_g, sdf_energy = self.compute_sdf_Hg(
                            i_iter, last_pose, cur_delta_pose, obs_xyz,
                            i_iter == -1)
                        loss_meter.append_loss({'sdf': sdf_energy})
                        cur_energy += sdf_energy
                        if i_iter != -1:
                            H += sdf_H
                            g += sdf_g
                    if loss_config[0] == 'rgb':
                        pyramid_level = loss_config[1]
                        rgb_H, rgb_g, rgb_energy = self.compute_rgb_Hg(
                            pyramid_level, cur_delta_pose,
                            cur_intensity_pyramid, cur_depth_pyramid,
                            cur_dIdxy_pyramid, calib, i_iter == -1)
                        loss_meter.append_loss({'rgb': rgb_energy})
                        cur_energy += rgb_energy
                        if i_iter != -1:
                            H += rgb_H
                            g += rgb_g
                    if loss_config[0] == 'motion':
                        motion_H, motion_g, motion_energy = self.compute_motion_Hg(
                            cur_delta_pose, i_iter == -1)
                        loss_meter.append_loss({'motion': motion_energy})
                        cur_energy += motion_energy
                        if i_iter != -1:
                            H += motion_H
                            g += motion_g

                if cur_energy > last_energy:
                    cur_delta_pose = last_delta_pose
                    break
                else:
                    last_delta_pose = copy.deepcopy(cur_delta_pose)
                    last_energy = cur_energy

                if i_iter != -1:
                    xi = np.linalg.solve(H, -g)
                    cur_delta_pose = Isometry.from_twist(xi) @ cur_delta_pose
                    # logging.info(f"GN Iter {i_iter} @ Group {group_iter_config}, Loss = {loss_meter.get_printable_newest()}")

        if i_iter >= 10:
            # Safe bar: more number of iterations indicate bad convergence. This may be due to too small regularization,
            #       So we fallback to our default setting after this detection.
            self.n_unstable += 1
            if self.n_unstable >= 3:
                self.rgb_args.weight = max(self.rgb_args.weight, 500.)

        return last_pose.dot(cur_delta_pose)
Esempio n. 9
0
 def get_view_motions(view_id):
     return [Isometry.from_matrix(trans_dict[t][view_id]) for t in range(1, n_parts + 1)]