Пример #1
0
    def rectify_fusiello(self, d1=np.zeros(2), d2=np.zeros(2)):
        """
        Translation of Andre's IDL function rectify_fusello.
        """
        try:
            K1, R1, C1, _, _, _, _ = cv2.decomposeProjectionMatrix(self.view1.P)
            K2, R2, C2, _, _, _, _ = cv2.decomposeProjectionMatrix(self.view2.P)
        except:
            return

        C1 = cv2.convertPointsFromHomogeneous(C1.T).reshape(3, 1)
        C2 = cv2.convertPointsFromHomogeneous(C2.T).reshape(3, 1)

        oc1 = mdot(-R1.T, np.linalg.inv(K1), self.view1.P[:, 3])
        oc2 = mdot(-R2.T, np.linalg.inv(K2), self.view2.P[:, 3])

        v1 = (oc2-oc1).T
        v2 = np.cross(R1[2, :], v1)
        v3 = np.cross(v1, v2)

        R = np.array([v1/np.linalg.norm(v1), v2/np.linalg.norm(v2),
                      v3/np.linalg.norm(v3)]).reshape(3, 3)

        Kn1 = np.copy(K2)
        Kn1[0, 1] = 0
        Kn2 = np.copy(K2)
        Kn2[0, 1] = 0

        Kn1[0, 2] = Kn1[0, 2] + d1[0]
        Kn1[1, 2] = Kn1[1, 2] + d1[1]
        Kn2[0, 2] = Kn2[0, 2] + d2[0]
        Kn2[1, 2] = Kn2[1, 2] + d2[1]

        t1 = np.matmul(-R, C1)
        t2 = np.matmul(-R, C2)
        Rt1 = np.concatenate((R, t1), 1)
        Rt2 = np.concatenate((R, t2), 1)
        Prec1 = np.dot(Kn1, Rt1)
        Prec2 = np.dot(Kn2, Rt2)

        Tr1 = np.dot(Prec1[:3, :3], np.linalg.inv(self.view1.P[:3, :3]))
        Tr2 = np.dot(Prec2[:3, :3], np.linalg.inv(self.view2.P[:3, :3]))
        return Prec1, Prec2, Tr1, Tr2
Пример #2
0
    def euler_jacobian(P, H, X, x):
        """
        Constructs the Euler Jacobian used in GN_estimation.
        """
        n_landmarks = X.shape[0]
        J = np.ones((2*n_landmarks, 6))

        for i in range(6):
            h = mat2vec(H)
            h[i] = h[i] + 0.5
            H_new = vec2mat(h)
            x_new = mdot(P, H_new, X.T)
            x_new = np.apply_along_axis(lambda v: v/v[-1], 0, x_new)

            J[:, i] = ((x_new - x)/0.5)[:2, :].flatten(order='F')
        return J
Пример #3
0
    def iterate_jacobian(self, view_number, H, key_index, db_index,
                         outlier_threshold=2, iter_number=0):
        """
        Function called in main loop of GN_estimation.
        """
        if len(db_index):

            if view_number == 1:
                key_coords = self.view1.key_coords[key_index]
                P = self.view1.P
            elif view_number == 2:
                key_coords = self.view2.key_coords[key_index]
                P = self.view2.P

            db_landmarks = self.database.landmarks[db_index]
            projections = mdot(P, H, db_landmarks.T)
            projections = np.apply_along_axis(lambda v: v/v[-1], 0, projections)

            J = self.euler_jacobian(P, H, db_landmarks, projections)
            squErr = np.sqrt(np.sum(
                     np.square(projections[:2, :] - key_coords.T), 0))
            outliers = detect_outliers(squErr)

            if outliers.shape:
                key_index= np.delete(key_index, outliers, axis=0)
                projections = np.delete(projections, outliers, axis=1)
                db_index = np.delete(db_index, outliers, axis=0)
                Joutliers = np.array([2*outliers,
                                      (2*outliers + 1)]).flatten()
                J = np.delete(J, Joutliers, axis=0)
                squErr = np.delete(squErr, outliers)
            if iter_number >= 6:
                abs_outliers = np.where(squErr > outlier_threshold)[0]
                if len(abs_outliers) > 0:
                    key_index = np.delete(key_index, abs_outliers, axis=0)
                    projections = np.delete(projections, abs_outliers, axis=1)
                    db_index = np.delete(db_index, abs_outliers, axis=0)
                    Joutliers = np.array([2*abs_outliers,
                                          (2*abs_outliers + 1)]).flatten()
                    J = np.delete(J, Joutliers, axis=0)
        else:
            J = np.array([])
            projections = np.array([])
            key_index = np.array([], dtype=int)
        return J, projections, key_index, db_index
Пример #4
0
    def estimate_pose_gn(self, X, frameDescriptors, in1, in2, n_iterations=10,
         abs_pix_thresh=2):
        """
        Matches keypoints from view1 and view2 to database indepedently. If
        there are matches, calls GN_estimation() to calculate pose. Retuns time
        taken to match keypoints with database, time taken to estimate pose,
        and number of keypoints in view1 and view2 used to calculte pose.
        """
        flag = 0
        matchDBStart = time.perf_counter()

        # if self.view1.descriptors and len(self.database):
        matches_view1db = self.match_descriptors(self.database.descriptors,
                                                 self.view1.descriptors,
                                                 matching_type='database')
        # else:
        #     matches_view1db
        # if self.view2.descriptors and len(self.database):
        matches_view2db = self.match_descriptors(self.database.descriptors,
                                                 self.view2.descriptors,
                                                 matching_type='database')
        # frameIdx_raw and dbIdx_raw contain duplicate/unreliable matches. We
        # retain these indices as we add the landmarks with indices
        # complementary to these raw indices to the database. For pose
        # estimation however, we use frameIdx and dbIdx, which don't contain
        # duplicates.

        dbIdx1_raw, frameIdx1_raw = self.extract_match_indices(matches_view1db)
        dbIdx2_raw, frameIdx2_raw = self.extract_match_indices(matches_view2db)

        self.matches_db_view1 = matches_view1db
        self.matches_db_view2 = matches_view2db

        matches_view1db = self.remove_duplicate_matches(matches_view1db)
        matches_view2db = self.remove_duplicate_matches(matches_view2db)

        dbIdx1, frameIdx1 = self.extract_match_indices(matches_view1db)
        dbIdx2, frameIdx2 = self.extract_match_indices(matches_view2db)
        print('Number of db matches view1:', len(frameIdx1))
        print('Number of db matches view2:', len(frameIdx2))
        matchDBTime = time.perf_counter() - matchDBStart

        # Estimate pose
        if (len(frameIdx1) and len(frameIdx2)): # Landmarks seen in both views
            poseEstTime, key_index1, key_index2, \
            used_landmarks1, used_landmarks2, flag = \
                self.GN_estimation(
                    frameIdx1,
                    frameIdx2,
                    dbIdx1,
                    dbIdx2,
                    n_iterations,
                    abs_pix_thresh
                )
        elif len(frameIdx1): # Landmarks seen in view 1 only
            poseEstTime, key_index1, key_index2, \
            used_landmarks1, used_landmarks2, flag = \
                self.GN_estimation(
                    frameIdx1,
                    np.array([], dtype=int),
                    dbIdx1,
                    np.array([], dtype=int),
                    n_iterations,
                    abs_pix_thresh
                )
        elif len(frameIdx2): # Landmarks seen in view 2 only
            poseEstTime, key_index1, key_index2, \
            used_landmarks1, used_landmarks2, flag = \
                self.GN_estimation(
                    np.array([], dtype=int),
                    frameIdx2,
                    np.array([], dtype=int),
                    dbIdx2,
                    n_iterations,
                    abs_pix_thresh
                )
        else:
            print('No matches with database, returning previous pose.\n')
            used_landmarks1 = np.array([], dtype=int)
            used_landmarks2 = np.array([], dtype=int)
            poseEstTime = 0
            flag = 1
            return matchDBTime, poseEstTime, used_landmarks1, used_landmarks2, \
                   flag

        H = vec2mat(self.currentPose)
        # Add new entries to database
        new_landmarks = []
        old_landmarks = []
        self.used_key_indices_1 = key_index1
        self.used_key_indices_2 = key_index2

        if self.update_db_cutoff is None or self.frameNumber < self.update_db_cutoff:
            if len(in1) and flag == 0:
                for i in range(len(in1)):
                    if (in1[i] not in frameIdx1_raw) and (in2[i] not in frameIdx2_raw):
                        new_landmarks.append(i)
                    # elif (in1[i] in key_index1) and (in2[i] in key_index2):
                    #     db_update_index1 = used_landmarks1[np.where(key_index1 == in1[i])[0]]
                    #     db_update_index2 = used_landmarks2[np.where(key_index2 == in2[i])[0]]
                    #     if db_update_index1 == db_update_index2:
                    #         db_update_index = db_update_index1
                    #         self.database.landmarks[db_update_index] = np.dot(np.linalg.inv(H), X[i, :].T).T
                    #         self.database.descriptors[db_update_index] = frameDescriptors[i, :]

                X_new = X[new_landmarks, :]
                descriptors_new = frameDescriptors[new_landmarks, :]
                X_new = mdot(np.linalg.inv(H), X_new.T).T
                self.database.update(X_new, descriptors_new)

        return matchDBTime, poseEstTime, used_landmarks1, used_landmarks2, \
               flag
Пример #5
0
    def estimate_pose_ls(self, X, frameDescriptors):
        """
        Estimate pose using Horn's method. Returns time taken to match
        descriptors with database, time taken to estimate pose, and number of
        3D landmarks used to estimate pose.
        """
        flag = 0
        # Match 3D points found in current frame with database
        matchDBStart = time.perf_counter()
        matches_db = self.match_descriptors(self.database.descriptors,
                                            frameDescriptors,
                                            matching_type='database')
        # frameIdx_raw and dbIdx_raw contain duplicate/unreliable matches. We
        # retain these indices as we add the landmarks with indices
        # complementary to these raw indices to the database. For pose
        # estimation however, we use frameIdx and dbIdx, which don't contain
        # duplicates.
        dbIdx_raw, frameIdx_raw = self.extract_match_indices(matches_db)
        self.matches_db_view1 = matches_db
        self.matches_db_view2 = matches_db

        matches_db = self.remove_duplicate_matches(matches_db)
        dbIdx, frameIdx = self.extract_match_indices(matches_db)

        matchDBTime = time.perf_counter() - matchDBStart

        poseEstStart = time.perf_counter()
        if (len(frameIdx) >= 3 and len(dbIdx) >= 3):
            XMatched = X[frameIdx]
            frameDescriptorsMatched = frameDescriptors[frameIdx]
            landmarksMatched = self.database.landmarks[dbIdx]
            landmarkDescriptorsMatched = self.database.descriptors[dbIdx]

            H = self.hornmm(XMatched, landmarksMatched)

            # Outlier removal, recalculate pose
            squErr = np.sqrt(np.sum(np.square(
                (XMatched.T - np.dot(H, landmarksMatched.T))), 0))

            # Absolute outlier rejection
            outliers = np.where(squErr > 3)[0]

            # Relative outlier rejection
            # outliers = detect_outliers(squErr)
            XMatched = np.delete(XMatched, outliers, axis=0)
            db_index_used = np.delete(dbIdx, outliers, axis=0)
            landmarksMatched = np.delete(landmarksMatched, outliers, axis=0)

            if (len(XMatched) >= 3 and len(landmarksMatched >= 3)):
                H = self.hornmm(XMatched, landmarksMatched)

                pose_change = np.abs(mat2vec(H) - self.currentPose)
                # Reject new pose if change from previous pose is too high
                if (pose_change > self.pose_threshold).any():
                    print('Pose change larger than threshold, returning' +
                          ' previous pose')
                    db_index_used = np.array([], dtype=int)
                    flag = 1
                else:
                    # self.database.trim(dbIdx[outliers])
                    self.currentPose = mat2vec(H)

                    # Add new landmarks to database
                    landmarksNew = np.delete(X, frameIdx_raw, axis=0)
                    landmarkDescriptorsNew = np.delete(frameDescriptors, frameIdx_raw,
                                                       axis=0)
                    # Transform new landmark positions to original pose
                    landmarksNew = mdot(np.linalg.inv(H), landmarksNew.T).T
                    self.database.update(landmarksNew, landmarkDescriptorsNew)
                    usedKeypoints = len(XMatched)
                    print('USED KEYPOINTS PARAM:', usedKeypoints)
                    print('DB INDEX USED:', len(db_index_used))
            else:
                print('Not enough matches with database, returning previous pose\n')
                db_index_used = np.array([], dtype=int)
                flag = 1
        else:
            print('Not enough matches with database, returning previous pose\n')
            db_index_used = np.array([], dtype=int)
            flag = 1
        poseEstTime = time.perf_counter() - poseEstStart

        return matchDBTime, poseEstTime, db_index_used, flag