Esempio n. 1
0
    def __init__(self,pc_file_u,pc_file_v,
                 covfile,
                 covfile_sublayer=None,
                 pc_size=-1,
                 params={},
                 preset=None):
        """
        Initialize PCAFlow object.

        Parameters
        ----------
        pc_file_u, pc_file_v : string
            Files containing the principal components in horizontal and
            vertical direction, respectively.
            These files should be .npy files, in which each row is a flattened
            principal component (i.e., the total size of these principal
            component matrices is NUM_PC x (WIDTH*HEIGHT).

        cov_file : string
            File containing the covariance matrix of size NUM_PC x NUM_PC for 
            PCA-Flow.

        covfile_sublayer : string, optional
            File containing the covariance matrix for the layers (usually
            biased towards the first PCs).
            If PCA-Layers is used and this file is not given, use cov_file.

        pc_size : tuple, optional
            Size of principal components. Only required if PCs are not of size
            512x256 or 1024x436.

        params : dict, optional
            Parameters. See parameters.py for documentation of parameters.

        preset : string
            Preset with useful parameter values for different datasets.
            Can be one of
                'pcaflow_sintel'
                'pcalayers_sintel'
                'pcaflow_kitti'
                'pcalayers_kitti'

        """

        np.random.seed(1)

        self.params = defaults.get_parameters(params,preset)

        cprint('[PCAFlow] Initializing.', self.params)

        NC = int(self.params['NC'])
        self.NC = NC

        pc_u = np.load(pc_file_u)
        pc_v = np.load(pc_file_v)
        cov_matrix = np.load(covfile).astype('float32')

        if covfile_sublayer is not None:
            cov_matrix_sublayer = np.load(covfile_sublayer).astype('float32')
        else:
            cov_matrix_sublayer = None
       
        pc_w = 0
        pc_h = 0

        if pc_size==-1:
            # Try to guess principal component dimensions
            if pc_u.shape[1] == 1024*436:
                cprint('[PCAFLOW] Using PC dimensionality 1024 x 436', self.params)
                pc_w = 1024
                pc_h = 436
            elif pc_v.shape[1] == 512*256:
                cprint('[PCAFLOW] Using PC dimensionality 512 x 256', self.params)
                pc_w = 512
                pc_h = 256
            else:
                print('[PCAFLOW] *** ERROR *** ')
                print('[PCAFLOW] Could not guess dimensionality of principal components.')
                print('[PCAFLOW] Please provide as parameter.')
                sys.exit(1)


        self.PC = []

        # Smooth principal components.
        self.pc_u = self.filter_pcs(pc_u,(pc_w,pc_h)).astype('float32')
        self.pc_v = self.filter_pcs(pc_v,(pc_w,pc_h)).astype('float32')

        self.cov_matrix = cov_matrix
        
        self.pc_w = pc_w
        self.pc_h = pc_h

        self.reshape_features=True

        ###############################
        # Feature matcher
        ###############################
        if self.params['features'].lower() == 'libviso' and libviso_available:
            self.feature_matcher = FeatureMatcherLibviso(self.params)
        elif self.params['features'].lower() == 'orb':
            self.feature_matcher = FeatureMatcherORB(self.params)
        elif self.params['features'].lower() == 'fast':
            self.feature_matcher = FeatureMatcherFast(self.params)
        elif self.params['features'].lower() == 'akaze' or not libviso_available:
            self.feature_matcher = FeatureMatcherAKAZE(self.params)
        else:
            print('[PCAFLOW] *** ERROR ***')
            print('[PCAFLOW] Unknown feature type {}. Please use "libviso" or "fast".'.format(self.params['features']))
            sys.exit(1)

        if self.params['n_models'] <= 1:
            ##############################
            # Solver for PCA-Flow
            ##############################
            self.solver = RobustQuadraticSolver(self.pc_u,
                                                self.pc_v,
                                                self.cov_matrix,
                                                pc_size=(pc_w,pc_h),
                                                params=self.params)


        else:
            ############################## 
            # Solver for PCA-Layers
            ##############################  
            self.solver = EMSolver(self.pc_u, self.pc_v,
                                   self.cov_matrix,
                                   pc_size = (pc_w,pc_h),
                                   params=self.params,
                                   cov_matrix_sublayer=cov_matrix_sublayer)

        self.images = deque(maxlen=2)

        cprint('[PCAFLOW] Finished initializing.',self.params)
Esempio n. 2
0
class PCAFlow:
    """
    Basic PCAFlow class.

    """
    def __init__(self,pc_file_u,pc_file_v,
                 covfile,
                 covfile_sublayer=None,
                 pc_size=-1,
                 params={},
                 preset=None):
        """
        Initialize PCAFlow object.

        Parameters
        ----------
        pc_file_u, pc_file_v : string
            Files containing the principal components in horizontal and
            vertical direction, respectively.
            These files should be .npy files, in which each row is a flattened
            principal component (i.e., the total size of these principal
            component matrices is NUM_PC x (WIDTH*HEIGHT).

        cov_file : string
            File containing the covariance matrix of size NUM_PC x NUM_PC for 
            PCA-Flow.

        covfile_sublayer : string, optional
            File containing the covariance matrix for the layers (usually
            biased towards the first PCs).
            If PCA-Layers is used and this file is not given, use cov_file.

        pc_size : tuple, optional
            Size of principal components. Only required if PCs are not of size
            512x256 or 1024x436.

        params : dict, optional
            Parameters. See parameters.py for documentation of parameters.

        preset : string
            Preset with useful parameter values for different datasets.
            Can be one of
                'pcaflow_sintel'
                'pcalayers_sintel'
                'pcaflow_kitti'
                'pcalayers_kitti'

        """

        np.random.seed(1)

        self.params = defaults.get_parameters(params,preset)

        cprint('[PCAFlow] Initializing.', self.params)

        NC = int(self.params['NC'])
        self.NC = NC

        pc_u = np.load(pc_file_u)
        pc_v = np.load(pc_file_v)
        cov_matrix = np.load(covfile).astype('float32')

        if covfile_sublayer is not None:
            cov_matrix_sublayer = np.load(covfile_sublayer).astype('float32')
        else:
            cov_matrix_sublayer = None
       
        pc_w = 0
        pc_h = 0

        if pc_size==-1:
            # Try to guess principal component dimensions
            if pc_u.shape[1] == 1024*436:
                cprint('[PCAFLOW] Using PC dimensionality 1024 x 436', self.params)
                pc_w = 1024
                pc_h = 436
            elif pc_v.shape[1] == 512*256:
                cprint('[PCAFLOW] Using PC dimensionality 512 x 256', self.params)
                pc_w = 512
                pc_h = 256
            else:
                print('[PCAFLOW] *** ERROR *** ')
                print('[PCAFLOW] Could not guess dimensionality of principal components.')
                print('[PCAFLOW] Please provide as parameter.')
                sys.exit(1)


        self.PC = []

        # Smooth principal components.
        self.pc_u = self.filter_pcs(pc_u,(pc_w,pc_h)).astype('float32')
        self.pc_v = self.filter_pcs(pc_v,(pc_w,pc_h)).astype('float32')

        self.cov_matrix = cov_matrix
        
        self.pc_w = pc_w
        self.pc_h = pc_h

        self.reshape_features=True

        ###############################
        # Feature matcher
        ###############################
        if self.params['features'].lower() == 'libviso' and libviso_available:
            self.feature_matcher = FeatureMatcherLibviso(self.params)
        elif self.params['features'].lower() == 'orb':
            self.feature_matcher = FeatureMatcherORB(self.params)
        elif self.params['features'].lower() == 'fast':
            self.feature_matcher = FeatureMatcherFast(self.params)
        elif self.params['features'].lower() == 'akaze' or not libviso_available:
            self.feature_matcher = FeatureMatcherAKAZE(self.params)
        else:
            print('[PCAFLOW] *** ERROR ***')
            print('[PCAFLOW] Unknown feature type {}. Please use "libviso" or "fast".'.format(self.params['features']))
            sys.exit(1)

        if self.params['n_models'] <= 1:
            ##############################
            # Solver for PCA-Flow
            ##############################
            self.solver = RobustQuadraticSolver(self.pc_u,
                                                self.pc_v,
                                                self.cov_matrix,
                                                pc_size=(pc_w,pc_h),
                                                params=self.params)


        else:
            ############################## 
            # Solver for PCA-Layers
            ##############################  
            self.solver = EMSolver(self.pc_u, self.pc_v,
                                   self.cov_matrix,
                                   pc_size = (pc_w,pc_h),
                                   params=self.params,
                                   cov_matrix_sublayer=cov_matrix_sublayer)

        self.images = deque(maxlen=2)

        cprint('[PCAFLOW] Finished initializing.',self.params)



    def filter_pcs(self,matrix,size):
        """
        Apply Gaussian filter to principal components.
        This makes them somewhat better behaved.

        """

        matrix_out = np.zeros_like(matrix)

        #pdb.set_trace()

        for i,m in enumerate(matrix):
            m_ = m.reshape((size[1],size[0]))
            matrix_out[i,:] = cv2.GaussianBlur(m_,
                                               ksize=(0,0),
                                               sigmaX=size[0]/200.0).flatten()
        return matrix_out
        
        
    def push_back(self,I):
        """
        Push back frame.
        When processing a streaming video, this allows to pre-compute
        features only once per frame.

        Parameters
        ----------
        I : array_like
            Image, usually given as H x W x 3 color image.

        """
        cprint('[PCAFLOW] Adding image...', self.params)

        if not (I.shape[0] == self.pc_h and I.shape[1] == self.pc_w):
            self.reshape_features = True
            self.shape_I_orig = I.shape

        if self.params['image_blur'] > 0:
            I = cv2.GaussianBlur(
                    I,
                    ksize=(int(self.params['image_blur']),int(self.params['image_blur'])),
                    sigmaX=-1)

        cprint('[PCAFLOW] Adding image to feature matcher.', self.params)
        self.feature_matcher.push_back(I)
        self.images.append(I)
        cprint('[PCAFLOW] Done adding image.',self.params)

    def compute_flow(self,
                       kp1=None,kp2=None,
                       return_additional=[],
                       **kwargs
                      ):
        """
        Compute the flow.

        Parameters
        ----------
        kp1, kp2 : array_like, shape (NUM_KP,2), optional
            Matrices containing keypoints in image coordinates for
            first and second frame, respectively.
            The first column of both matrices contains the x coordinates,
            the second contains the y coordinates.
            If kp1 and kp2 are given, no additional feature matching is
            performed.
        
        return_additional: array of strings, optional.
            If set, return additional data. Possible entries are:
        
                'weights'   : Return flow coefficients
                'keypoints' : Return matched feature points
                'keypoint_labels' : Return assigned layers for keypoints
                                    (PCA-Layers only).
                'segments'  : Return segmentation map
                              (PCA-Layers only)
                'segment_flows' : For each layer, return flow.
                                  (PCA-Layers only)
        
            The additional data is returned as a dict with the same keys.
        
            Example:
                u,v,data = pcaflow.compute_flow(return_additional=['weights',])
                weights = data['weights']



        Returns
        -------
        u, v : array_like
            U and V flow fields.

        data_additional : dict, optional
            See above for details. The return formats are:

                'weights' : array_like, shape (NUM_PC,)
                'keypoints' : tuple (array_like, array_like)
                              Each array has shape (NUM_KP,2).
                'keypoint_labels' : array_like, shape (NUM_KP,)
                'segments' : array_like, shape (WIDTH,HEIGHT)
                'segment_flows' : array_like, shape (WIDTH, HEIGHT, 2, NUM_LAYERS)

        """

        # Parse return_additional.
        return_weights = False
        return_keypoints = False
        return_keypoint_labels = False
        return_segments = False
        return_segment_flows = False
        
        if 'weights' in return_additional:
            return_weights = True
        if 'keypoints' in return_additional:
            return_keypoints = True
        if 'keypoint_labels' in return_additional:
            return_keypoint_labels = True
        if 'segments' in return_additional:
            return_segments = True
        if 'segment_flows' in return_additional:
            return_segment_flows = True
            

        if kp1 is not None and kp2 is not None:
            # We got some initial features.
            kp1_ = kp1.copy()
            kp2_ = kp2.copy()

        else:
            kp1_,kp2_ = self.feature_matcher.get_features()

        if len(kp1_) == 0:
            print('[PCAFlow] Warning: No features found. Setting flow to 0.')
            u = np.zeros(self.shape_I_orig[:2])
            v = np.zeros_like(u)
            return (u,v)

        if self.params['remove_homography'] == 1:
            cprint('[PCAFlow] Removing homography...', self.params)

            kp1_h, kp2_h, H, H_inv, inliers_ = ht.remove_homography_from_points(kp1_,kp2_)

            dists_new = np.sqrt(np.sum((kp1_h - kp2_h)**2,axis=1))
            inliers = dists_new < 2
            kp1_ = kp1_h
            kp2_ = kp2_h
            #kp1[inliers,:] = kp0[inliers,:]
            I1_warped = cv2.warpPerspective(self.images[1],
                    H,
                    (self.images[1].shape[1],self.images[1].shape[0]),
                    flags=cv2.WARP_INVERSE_MAP+cv2.INTER_LINEAR,
                    borderMode=cv2.BORDER_REPLICATE,
                    )
        elif self.params['remove_homography'] == 2:
            cprint('[PCAFlow] Computing homography...', self.params)

            kp1_h, kp2_h, H, H_inv, inliers_ = ht.remove_homography_from_points(kp1_,kp2_)

            dists_new = np.sqrt(np.sum((kp1_h - kp2_h)**2,axis=1))
            inliers = dists_new < 2
            I1_warped = self.images[1]

        else:
            inliers = None
            I1_warped = self.images[1]
            H = None

        kp1_orig = kp1_.copy()
        kp2_orig = kp2_.copy()

        if self.reshape_features:
            h_orig,w_orig = self.shape_I_orig[:2]
            h_orig_f = float(h_orig)
            w_orig_f = float(w_orig)
            scale = [self.pc_w / w_orig_f, self.pc_h / h_orig_f]
            kp1_ *= scale
            kp2_ *= scale
            I0_ = cv2.resize(self.images[0],(self.pc_w,self.pc_h))
            I1_ = cv2.resize(I1_warped,(self.pc_w,self.pc_h))
        else:
            I0_ = self.images[0]
            I1_ = I1_warped

        cprint('[PCAFLOW] %s features detected...'%kp1_.shape[0], self.params)

        # Solve
        if self.params['n_models'] > 1:
            u_,v_,weights,data_additional_em = self.solver.solve(kp1_,kp2_,
                    I0=I0_,
                    I1=I1_,
                    inliers=inliers,
                    H=H,
                    shape_I_orig=self.shape_I_orig,
                    return_additional=return_additional,
                    **kwargs)
        else:
            if return_weights:
                u_,v_,weights = self.solver.solve(kp1_,kp2_,return_coefficients=True)
            else:
                u_,v_ = self.solver.solve(kp1_,kp2_)
            data_additional_em = {}

        if self.reshape_features:
            u = cv2.resize(u_,(w_orig,h_orig))
            v = cv2.resize(v_,(w_orig,h_orig))

            u *= w_orig_f / self.pc_w
            v *= h_orig_f / self.pc_h

        if self.params['remove_homography']==1:
            cprint('[PCAFlow] Re-applying homography...', self.params)
            u2,v2 = ht.apply_homography_to_flow(u,v,H)
            u = u2
            v = v2

        if len(return_additional) == 0:
            return u,v

        else:
            # Return more additional data
            data_additional = {}
            if return_weights:
                data_additional['weights'] = weights
            if return_keypoints:
                data_additional['keypoints'] = (kp1_orig,kp2_orig)

            # Get additional data from EMSolver
            for key,value in data_additional_em.items():
                data_additional[key] = value

            return u, v, data_additional