@@ -35,8 +35,9 @@ class BayesianCoherentPointDrift:
3535 source (numpy.ndarray, optional): Source point cloud data.
3636 """
3737
38- def __init__ (self , source = None ):
38+ def __init__ (self , source = None , use_debias = False ):
3939 self ._source = source
40+ self ._use_debias = use_debias
4041 self ._tf_type = None
4142 self ._callbacks = []
4243
@@ -58,14 +59,20 @@ def expectation_step(self, t_source, target, scale, alpha, sigma_mat, sigma2, w=
5859 pmat = np .exp (- pmat / (2.0 * sigma2 ))
5960 pmat /= (2.0 * np .pi * sigma2 ) ** (dim * 0.5 )
6061 pmat = pmat .T
61- pmat *= np .exp (- (scale ** 2 ) / (2 * sigma2 ) * np .diag (sigma_mat ) * dim )
62+ if self ._use_debias and sigma_mat is not None :
63+ pmat *= np .exp (- (scale ** 2 ) / (2 * sigma2 ) * np .diag (sigma_mat ) * dim )
6264 pmat *= (1.0 - w ) * alpha
63- den = w / target .shape [0 ] + np .sum (pmat , axis = 1 )
65+ bb_range = target .max (axis = 0 ) - target .min (axis = 0 )
66+ bb_range = np .maximum (bb_range , 1e-10 )
67+ volume = np .prod (bb_range )
68+ outlier_term = w / volume
69+ den = outlier_term + np .sum (pmat , axis = 1 )
6470 den [den == 0 ] = np .finfo (np .float32 ).eps
6571 pmat = np .divide (pmat .T , den )
6672
6773 nu_d = np .sum (pmat , axis = 0 )
6874 nu = np .sum (pmat , axis = 1 )
75+ nu = np .maximum (nu , 1e-20 )
6976 nu_inv = 1.0 / np .kron (nu , np .ones (dim ))
7077 px = np .dot (np .kron (pmat , np .identity (dim )), target .ravel ())
7178 x_hat = np .multiply (px , nu_inv ).reshape (- 1 , dim )
@@ -101,32 +108,39 @@ def registration(self, target, w=0.0, maxiter=50, tol=0.001):
101108
102109
103110class CombinedBCPD (BayesianCoherentPointDrift ):
104- def __init__ (self , source = None , lmd = 2.0 , k = 1.0e20 , gamma = 1.0 ):
105- super (CombinedBCPD , self ).__init__ (source )
111+ def __init__ (self , source = None , lmd = 2.0 , k = 1.0e20 , gamma = 1.0 , beta = 1.0 , kernel = "imq" , use_debias = False ):
112+ super (CombinedBCPD , self ).__init__ (source , use_debias )
106113 self ._tf_type = tf .CombinedTransformation
107114 self .lmd = lmd
108115 self .k = k
109116 self .gamma = gamma
117+ self .beta = beta
118+ self .kernel = kernel
110119
111120 def _initialize (self , target ):
112121 m , dim = self ._source .shape
113- self .gmat = mu .inverse_multiquadric_kernel (self ._source , self ._source )
122+ if self .kernel == "imq" :
123+ self .gmat = mu .inverse_multiquadric_kernel (self ._source , self ._source , self .beta )
124+ elif self .kernel == "rbf" :
125+ self .gmat = mu .rbf_kernel (self ._source , self ._source , self .beta )
126+ else :
127+ raise ValueError ("Invalid kernel type: %s" % self .kernel )
114128 self .gmat_inv = np .linalg .inv (self .gmat )
115- sigma2 = self .gamma * mu .squared_kernel_sum (self ._source , target )
129+ sigma2 = ( self .gamma ** 2 ) * mu .squared_kernel_sum (self ._source , target )
116130 q = 1.0 + target .shape [0 ] * dim * 0.5 * np .log (sigma2 )
117131 return MstepResult (self ._tf_type (np .identity (dim ), np .zeros (dim )), None , np .identity (m ), 1.0 / m , sigma2 )
118132
119133 def maximization_step (self , target , rigid_trans , estep_res , sigma2_p = None ):
120134 return self ._maximization_step (
121- self ._source , target , rigid_trans , estep_res , self .gmat_inv , self .lmd , self .k , sigma2_p
135+ self ._source , target , rigid_trans , estep_res , self .gmat_inv , self .lmd , self .k , sigma2_p , self . _use_debias
122136 )
123137
124138 @staticmethod
125- def _maximization_step (source , target , rigid_trans , estep_res , gmat_inv , lmd , k , sigma2_p = None ):
139+ def _maximization_step (source , target , rigid_trans , estep_res , gmat_inv , lmd , k , sigma2_p = None , use_debias = False ):
126140 nu_d , nu , n_p , px , x_hat = estep_res
127141 dim = source .shape [1 ]
128142 m = source .shape [0 ]
129- s2s2 = rigid_trans .scale ** 2 / ( sigma2_p ** 2 )
143+ s2s2 = rigid_trans .scale ** 2 / sigma2_p
130144 sigma_mat_inv = lmd * gmat_inv + s2s2 * np .diag (nu )
131145 sigma_mat = np .linalg .inv (sigma_mat_inv )
132146 residual = rigid_trans .inverse ().transform (x_hat ) - source
@@ -140,19 +154,24 @@ def _maximization_step(source, target, rigid_trans, estep_res, gmat_inv, lmd, k,
140154 u_m = np .sum (nu * u_hat .T , axis = 1 ) / n_p
141155 u_hm = u_hat - u_m
142156 s_xu = np .matmul (np .multiply (nu , (x_hat - x_m ).T ), u_hm ) / n_p
143- s_uu = np .matmul (np .multiply (nu , u_hm .T ), u_hm ) / n_p + sigma2_m * np .identity (dim )
157+ s_uu = np .matmul (np .multiply (nu , u_hm .T ), u_hm ) / n_p
158+ if use_debias :
159+ s_uu += sigma2_m * np .identity (dim )
144160 phi , _ , psih = np .linalg .svd (s_xu , full_matrices = True )
145161 c = np .ones (dim )
146162 c [- 1 ] = np .linalg .det (np .dot (phi , psih ))
147163 rot = np .matmul (phi * c , psih )
148- tr_rsxu = np .trace ( np . matmul ( rot , s_xu ) )
164+ tr_rsxu = np .sum ( rot * s_xu )
149165 scale = tr_rsxu / np .trace (s_uu )
150166 t = x_m - scale * np .dot (rot , u_m )
151- y_hat = rigid_trans . transform ( source + v_hat )
167+ y_hat = scale * np . dot ( u_hat , rot . T ) + t
152168 s1 = np .dot (target .ravel (), np .kron (nu_d , np .ones (dim )) * target .ravel ())
153169 s2 = np .dot (px .ravel (), y_hat .ravel ())
154170 s3 = np .dot (y_hat .ravel (), np .kron (nu , np .ones (dim )) * y_hat .ravel ())
155- sigma2 = (s1 - 2.0 * s2 + s3 ) / (n_p * dim ) + scale ** 2 * sigma2_m
171+ sigma2 = (s1 - 2.0 * s2 + s3 ) / (n_p * dim )
172+ if use_debias :
173+ sigma2 += rigid_trans .scale ** 2 * sigma2_m
174+ sigma2 = max (abs (sigma2 ), np .finfo (np .float64 ).eps )
156175 return MstepResult (tf .CombinedTransformation (rot , t , scale , v_hat ), u_hat , sigma_mat , alpha , sigma2 )
157176
158177
0 commit comments