@@ -35,11 +35,13 @@ def _rotratio_eulerangle_vec(self, clmatrix, i, j, good_k, n_theta):
3535 # cl_diff2 is for the angle on C2 created by its intersection with C1 and C3.
3636 # cl_diff3 is for the angle on C3 created by its intersection with C2 and C1.
3737 cl_diff1 = clmatrix [i , good_k ] - clmatrix [i , j ] # for theta1
38- cl_diff2 = clmatrix [j , good_k ] - clmatrix [j , i ] # for - theta2
38+ cl_diff2 = clmatrix [j , good_k ] - clmatrix [j , i ] # for theta2
3939 cl_diff3 = clmatrix [good_k , j ] - clmatrix [good_k , i ] # for theta3
4040
4141 # Calculate the cos values of rotation angles between i an j images for good k images
42- c_alpha , good_idx = self ._get_cos_phis (cl_diff1 , cl_diff2 , cl_diff3 , n_theta )
42+ c_alpha , good_idx = self ._get_cos_phis (
43+ cl_diff1 , cl_diff2 , cl_diff3 , n_theta , sync = True
44+ )
4345 if len (c_alpha ) == 0 :
4446 return None
4547 alpha = np .arccos (c_alpha )
@@ -82,6 +84,7 @@ def _vote_ij(self, clmatrix, n_theta, i, j, k_list):
8284 k_list = k_list [
8385 (k_list != i ) & (clmatrix [i , k_list ] != - 1 ) & (clmatrix [j , k_list ] != - 1 )
8486 ]
87+
8588 cl_idx13 = clmatrix [i , k_list ]
8689 cl_idx31 = clmatrix [k_list , i ]
8790 cl_idx23 = clmatrix [j , k_list ]
@@ -94,12 +97,16 @@ def _vote_ij(self, clmatrix, n_theta, i, j, k_list):
9497 # cl_diff3 is for the angle on C3 created by its intersection with C2 and C1.
9598 cl_diff1 = cl_idx13 - cl_idx12
9699 # bad or just a trig identity?
97- cl_diff2 = cl_idx21 - cl_idx23
98- # cl_diff2 = cl_idx23 - cl_idx21 # theta2 = (clmatrix(j,K)-clmatrix(j,i)) * 2*pi/L;
100+ # cl_diff2 = cl_idx21 - cl_idx23
101+ cl_diff2 = (
102+ cl_idx23 - cl_idx21
103+ ) # theta2 = (clmatrix(j,K)-clmatrix(j,i)) * 2*pi/L;
99104 cl_diff3 = cl_idx32 - cl_idx31
100105
101106 # Calculate the cos values of rotation angles between i an j images for good k images
102- cos_phi2 , good_idx = self ._get_cos_phis (cl_diff1 , cl_diff2 , cl_diff3 , n_theta )
107+ cos_phi2 , good_idx = self ._get_cos_phis (
108+ cl_diff1 , cl_diff2 , cl_diff3 , n_theta , sync = True
109+ )
103110
104111 if np .any (np .abs (cos_phi2 ) - 1 > 1e-12 ):
105112 logger .warning (
@@ -115,28 +122,23 @@ def _vote_ij(self, clmatrix, n_theta, i, j, k_list):
115122 inds = k_list [good_idx ]
116123
117124 if phis .shape [0 ] == 0 :
118- return []
125+ return None , []
119126
120127 # Parameters used to compute the smoothed angle histogram.
121128 ntics = int (180 / self .hist_bin_width )
122- angles_grid = np .linspace (0 , 180 , ntics , True )
129+ angles_grid = np .linspace (0 , 180 , ntics + 1 , True )
123130 # Get angles between images i and j for computing the histogram
124131 angles = np .arccos (phis [:]) * 180 / np .pi
132+
125133 # Angles that are up to 10 degrees apart are considered
126134 # similar. This sigma ensures that the width of the density
127135 # estimation kernel is roughly 10 degrees. For 15 degrees, the
128136 # value of the kernel is negligible.
129137 sigma = 3.0
130138
131139 # Compute the histogram of the angles between images i and j
132- squared_values = np .add .outer (np .square (angles ), np .square (angles_grid ))
133- angles_hist = np .sum (
134- np .exp (
135- (2 * np .multiply .outer (angles , angles_grid ) - squared_values )
136- / (2 * sigma ** 2 )
137- ),
138- 0 ,
139- )
140+ angles_distances = angles_grid [None , :] - angles [:, None ]
141+ angles_hist = np .sum (np .exp (- (angles_distances ** 2 ) / (2 * sigma ** 2 )), axis = 0 )
140142
141143 # We assume that at the location of the peak we get the true angle
142144 # between images i and j. Find all third images k, that induce an
@@ -169,10 +171,11 @@ def _vote_ij(self, clmatrix, n_theta, i, j, k_list):
169171 idx = np .abs (angles - angles_grid [peak_idx ]) < self .full_width
170172
171173 good_k = inds [idx ]
174+ alpha = np .arccos (phis [idx ])
172175
173- return good_k .astype ("int" )
176+ return alpha , good_k .astype ("int" )
174177
175- def _get_cos_phis (self , cl_diff1 , cl_diff2 , cl_diff3 , n_theta ):
178+ def _get_cos_phis (self , cl_diff1 , cl_diff2 , cl_diff3 , n_theta , sync = False ):
176179 """
177180 Calculate cos values of rotation angles between i and j images
178181
@@ -242,7 +245,29 @@ def _get_cos_phis(self, cl_diff1, cl_diff2, cl_diff3, n_theta):
242245
243246 # Calculated cos values of angle between i and j images
244247 cos_phi2 = (c3 [good_idx ] - c1 [good_idx ] * c2 [good_idx ]) / (
245- np .sin ( theta1 [good_idx ]) * np .sin ( theta2 [good_idx ])
248+ np .sqrt ( 1 - c1 [good_idx ] ** 2 ) * np .sqrt ( 1 - c2 [good_idx ] ** 2 )
246249 )
247250
251+ if sync :
252+ # % Some synchronization must be applied when common line is
253+ # % out by 180 degrees.
254+ # % Here fix the angles between c_ij(c_ji) and c_ik(c_jk) to be smaller than pi/2,
255+ # % otherwise there will be an ambiguity between alpha and pi-alpha.
256+ # ind1 = ( (theta1 > pi+TOL_idx) | (theta1<-TOL_idx & theta1>-pi) );
257+ # ind2 = ( (theta2 > pi+TOL_idx) | (theta2<-TOL_idx & theta2>-pi) );
258+ # align180 = (ind1 & ~ind2 ) | (~ind1 & ind2 );
259+ # cos_phi2(align180) = -cos_phi2(align180);
260+ TOL_idx = 1e-12
261+ theta1 = theta1 [good_idx ]
262+ theta2 = theta2 [good_idx ]
263+ theta3 = theta3 [good_idx ]
264+ ind1 = (theta1 > (np .pi + TOL_idx )) | (
265+ (theta1 < - TOL_idx ) & (theta1 > - np .pi )
266+ )
267+ ind2 = (theta2 > (np .pi + TOL_idx )) | (
268+ (theta2 < - TOL_idx ) & (theta2 > - np .pi )
269+ )
270+ align180 = (ind1 & ~ ind2 ) | (~ ind1 & ind2 )
271+ cos_phi2 [align180 ] = - cos_phi2 [align180 ]
272+
248273 return cos_phi2 , good_idx
0 commit comments