def distanceOfShifts(self, oldAlignmentList): """ distanceOfShifts: Determines distance of current shifts from previous shifts @param oldAlignmentList: """ from pytom.tools.maths import euclidianDistance, listMean, listStd distance = [] numberResults = len(self) oldListXML = oldAlignmentList.toXML() for i in xrange(0, numberResults): newResult = self[i] particle = newResult.getParticle() try: oldResultXML = oldListXML.xpath( '/AlignmentList/Result/Particle[@Filename="' + particle.getFilename() + '"]/..') oldResult = MaximisationResult() oldResult.fromXML(oldResultXML[0]) except: numberResults = numberResults - 1 continue newShift = newResult.getShift() oldShift = oldResult.getShift() d = euclidianDistance(newShift.toVector(), oldShift.toVector()) distance.append(d) return [listMean(distance), listStd(distance)]
def vector2euler(vec, reference_vec=[0,0,1]): """Transform a vector to an Euler angle representation. Or: find the Euler angle to rotate the reference vector to the given vector. Note there are infinite possible ways to do this, since the inplane rotation is not specified. """ from pytom.tools.maths import euclidianDistance if(euclidianDistance(vec, [0,0,0]) == 0): raise RuntimeError("Vector length should be bigger than 0!") if(euclidianDistance(vec, reference_vec) == 0): from pytom.basic.structures import Rotation return Rotation(0,0,0) import numpy as np vec = vec/np.linalg.norm(vec) axis = np.cross(reference_vec, vec) angle = np.math.acos(np.dot(vec, reference_vec)) mat = axisAngleToMat(axis, angle, True) rotation = matToZXZ(mat) return rotation
def frm_constrained_align(vf, wf, vg, wg, b, max_freq, peak_offset=None, mask=None, constraint=None, weights=None, position=None, num_seeds=5, pytom_volume=None): """Find the best alignment (translation & rotation) of volume vg (reference) to match vf. For details, please check the paper. Parameters ---------- vf: Volume Nr. 1 pytom_volume.vol wf: Mask of vf in Fourier space. pytom.basic.structures.Wedge. If none, no missing wedge. vg: Volume Nr. 2 / Reference pytom_volume.vol wg: Mask of vg in Fourier space. pytom.basic.structures.Wedge. If none, no missing wedge. b: Bandwidth range of spherical harmonics. None -> [4, 64] List -> [b_min, b_max] Integer -> [b, b] max_freq: Maximal frequency involved in calculation. Integer. peak_offset: The maximal offset which allows the peak of the score to be. Or simply speaking, the maximal distance allowed to shift vg to match vf. This parameter is needed to prevent shifting the reference volume out of the frame. pytom_volume.vol / Integer. By default is half of the volume radius. mask: Mask volume for vg in real space. pytom_volume.vol constraint: Angular constraint sh_alignment.constrained_frm.AngularConstraint weights: Obsolete. position: If the translation is already known or not. If provided, no translational search will be conducted. List: [x,y,z], default None. num_seeds: Number of threads for the expectation maximization procedure. The more the better, yet slower. Integer, default is 5. Returns ------- (The best translation and rotation (Euler angle, ZXZ convention [Phi, Psi, Theta]) to transform vg to match vf. (best_translation, best_rotation, correlation_score) """ from pytom_volume import vol, rotateSpline, peak from pytom.basic.transformations import shift from pytom.basic.correlation import FLCF from pytom.basic.filter import lowpassFilter from pytom.basic.structures import Mask, SingleTiltWedge from pytom_volume import initSphere from pytom_numpy import vol2npy if vf.sizeX() != vg.sizeX() or vf.sizeY() != vg.sizeY() or vf.sizeZ( ) != vg.sizeZ(): raise RuntimeError('Two volumes must have the same size!') if wf is None: wf = SingleTiltWedge(0) if wg is None: wg = SingleTiltWedge(0) if peak_offset is None: peak_offset = vol(vf.sizeX(), vf.sizeY(), vf.sizeZ()) initSphere(peak_offset, vf.sizeX() / 4, 0, 0, vf.sizeX() / 2, vf.sizeY() / 2, vf.sizeZ() / 2) elif peak_offset.__class__ == int: peak_radius = peak_offset peak_offset = vol(vf.sizeX(), vf.sizeY(), vf.sizeZ()) initSphere(peak_offset, peak_radius, 0, 0, vf.sizeX() / 2, vf.sizeY() / 2, vf.sizeZ() / 2) elif peak_offset.__class__ == vol: pass else: raise RuntimeError('Peak offset is given wrong!') # cut out the outer part which normally contains nonsense m = vol(vf.sizeX(), vf.sizeY(), vf.sizeZ()) initSphere(m, vf.sizeX() / 2, 0, 0, vf.sizeX() / 2, vf.sizeY() / 2, vf.sizeZ() / 2) vf = vf * m vg = vg * m if mask is None: mask = m else: vg = vg * mask if position is None: # if position is not given, we have to find it ourself # first roughtly determine the orientation (only according to the energy info) # get multiple candidate orientations numerator, denominator1, denominator2 = frm_correlate( vf, wf, vg, wg, b, max_freq, weights, True, None, None, False) score = numerator / (denominator1 * denominator2)**0.5 res = frm_find_topn_constrained_angles_interp( score, num_seeds, get_adaptive_bw(max_freq, b) / 16., constraint) else: # the position is given by the user vf2 = shift(vf, -position[0] + vf.sizeX() / 2, -position[1] + vf.sizeY() / 2, -position[2] + vf.sizeZ() / 2, 'fourier') score = frm_correlate(vf2, wf, vg, wg, b, max_freq, weights, ps=False) orientation, max_value = frm_find_best_constrained_angle_interp( score, constraint=constraint) return position, orientation, max_value # iteratively refine the position & orientation from pytom.tools.maths import euclidianDistance max_iter = 10 # maximal number of iterations mask2 = vol(mask.sizeX(), mask.sizeY(), mask.sizeZ()) # store the rotated mask vg2 = vol(vg.sizeX(), vg.sizeY(), vg.sizeZ()) lowpass_vf = lowpassFilter(vf, max_freq, max_freq / 10.)[0] max_position = None max_orientation = None max_value = -1.0 for i in xrange(num_seeds): old_pos = [-1, -1, -1] lm_pos = [-1, -1, -1] lm_ang = None lm_value = -1.0 orientation = res[i][0] # initial orientation for j in xrange(max_iter): rotateSpline(vg, vg2, orientation[0], orientation[1], orientation[2]) # first rotate rotateSpline(mask, mask2, orientation[0], orientation[1], orientation[2]) # rotate the mask as well vg2 = wf.apply(vg2) # then apply the wedge vg2 = lowpassFilter(vg2, max_freq, max_freq / 10.)[0] score = FLCF(lowpass_vf, vg2, mask2) # find the position pos = peak(score, peak_offset) pos, val = find_subpixel_peak_position(vol2npy(score), pos) if val > lm_value: lm_pos = pos lm_ang = orientation lm_value = val if euclidianDistance(lm_pos, old_pos) <= 1.0: # terminate this thread if lm_value > max_value: max_position = lm_pos max_orientation = lm_ang max_value = lm_value break else: old_pos = lm_pos # here we shift the target, not the reference # if you dont want the shift to change the energy landscape, use fourier shift vf2 = shift(vf, -lm_pos[0] + vf.sizeX() / 2, -lm_pos[1] + vf.sizeY() / 2, -lm_pos[2] + vf.sizeZ() / 2, 'fourier') score = frm_correlate(vf2, wf, vg, wg, b, max_freq, weights, False, denominator1, denominator2, True) orientation, val = frm_find_best_constrained_angle_interp( score, constraint=constraint) else: # no converge after the specified iteration, still get the best result as we can if lm_value > max_value: max_position = lm_pos max_orientation = lm_ang max_value = lm_value # print max_value # for show the convergence of the algorithm return max_position, max_orientation, max_value
shifty = np.random.randint(-offset, offset) shiftz = np.random.randint(-offset, offset) # first rotate and then shift v2 = fourier_rotate_vol(v, [phi, psi, the]) v2 = shift(v2, shiftx, shifty, shiftz, 'spline') # add some noise v2 = add(v2, 0.01) # finally apply the wedge v2 = wedge.apply(v2) t = Timing() # frm_match t.start() pos, ang = frm_align_vol(v2, [-60, 60], v, [8, 32], 10) t1 = t.end() dist_old = rotation_distance([phi, psi, the], ang) diff_old.append(dist_old) total_time1 += t1 print euclidianDistance([ shiftx + v.sizeX() / 2, shifty + v.sizeY() / 2, shiftz + v.sizeZ() / 2 ], pos), dist_old print print np.mean(diff_old) print total_time1 / 100
from frm import frm_align_vol from pytom.tools.maths import rotation_distance, euclidianDistance from pytom.tools.timing import Timing t = Timing() t.start() dis_offset = [] ang_offset = [] for pp in pl: v = read(pp.getFilename()) pos, ang, score = frm_align_vol(v, [-60.0, 60.0], r, [8, 32], 10) g_pos = pp.getShift().toVector() g_pos = [g_pos[0] + 50, g_pos[1] + 50, g_pos[2] + 50] g_ang = [ pp.getRotation().getZ1(), pp.getRotation().getZ2(), pp.getRotation().getX() ] dis_offset.append(euclidianDistance(g_pos, pos)) ang_offset.append(rotation_distance(g_ang, ang)) print euclidianDistance(g_pos, pos), rotation_distance(g_ang, ang) tt = t.end() import numpy as np print tt / 100, np.mean(dis_offset), np.mean(ang_offset)
def frm_align_vol_rscore(vf, wf, vg, wg, b, radius=None, mask=None, peak_offset=None, weights=None, position=None): """Obsolete. """ from pytom_volume import vol, rotateSpline, peak from pytom.basic.transformations import shift from pytom.basic.correlation import xcf from pytom.basic.filter import lowpassFilter from pytom.basic.structures import Mask from pytom_volume import initSphere from pytom_numpy import vol2npy if vf.sizeX()!=vg.sizeX() or vf.sizeY()!=vg.sizeY() or vf.sizeZ()!=vg.sizeZ(): raise RuntimeError('Two volumes must have the same size!') if mask is None: mask = vol(vf.sizeX(), vf.sizeY(), vf.sizeZ()) initSphere(mask, vf.sizeX()/2, 0,0, vf.sizeX()/2,vf.sizeY()/2,vf.sizeZ()/2) elif mask.__class__ == vol: pass elif mask.__class__ == Mask: mask = mask.getVolume() elif isinstance(mask, int): mask_radius = mask mask = vol(vf.sizeX(), vf.sizeY(), vf.sizeZ()) initSphere(mask, mask_radius, 0,0, vf.sizeX()/2,vf.sizeY()/2,vf.sizeZ()/2) else: raise RuntimeError('Given mask has wrong type!') if peak_offset is None: peak_offset = vol(vf.sizeX(), vf.sizeY(), vf.sizeZ()) initSphere(peak_offset, vf.sizeX()/2, 0,0, vf.sizeX()/2,vf.sizeY()/2,vf.sizeZ()/2) elif isinstance(peak_offset, int): peak_radius = peak_offset peak_offset = vol(vf.sizeX(), vf.sizeY(), vf.sizeZ()) initSphere(peak_offset, peak_radius, 0,0, vf.sizeX()/2,vf.sizeY()/2,vf.sizeZ()/2) elif peak_offset.__class__ == vol: pass else: raise RuntimeError('Peak offset is given wrong!') # cut out the outer part which normally contains nonsense vf = vf*mask # # normalize them first # from pytom.basic.normalise import mean0std1 # mean0std1(vf) # mean0std1(vg) if position is None: # if position is not given, we have to find it ourself # first roughtly determine the orientation (only according to the energy info) # get multiple candidate orientations orientations = frm_determine_orientation_rscore(vf, wf, vg, wg, b, radius, weights) else: # the position is given by the user vf2 = shift(vf, -position[0]+vf.sizeX()/2, -position[1]+vf.sizeY()/2, -position[2]+vf.sizeZ()/2, 'spline') res = frm_fourier_adaptive_wedge_vol_rscore(vf2, wf, vg, wg, b, radius, weights) orientation, max_value = frm_find_best_angle_interp(res) return position, orientation, max_value # iteratively refine the position & orientation from pytom.basic.structures import WedgeInfo from pytom.tools.maths import euclidianDistance max_iter = 10 # maximal number of iterations wedge = WedgeInfo([90+wf[0], 90-wf[1]]) old_pos = [-1, -1, -1] vg2 = vol(vg.sizeX(), vg.sizeY(), vg.sizeZ()) lowpass_vf = lowpassFilter(vf, radius, 0)[0] for i in range(max_iter): peak_value = 0.0 position = None for orientation in orientations: orientation = orientation[0] rotateSpline(vg, vg2, orientation[0], orientation[1], orientation[2]) # first rotate vg2 = wedge.apply(vg2) # then apply the wedge vg2 = lowpassFilter(vg2, radius, 0)[0] res = xcf(lowpass_vf, vg2) # find the position pos = peak(res, peak_offset) # val = res(pos[0], pos[1], pos[2]) pos, val = find_subpixel_peak_position(vol2npy(res), pos) if val > peak_value: position = pos peak_value = val if euclidianDistance(position, old_pos) <= 1.0: break else: old_pos = position # here we shift the target, not the reference # if you dont want the shift to change the energy landscape, use fourier shift vf2 = shift(vf, -position[0]+vf.sizeX()/2, -position[1]+vf.sizeY()/2, -position[2]+vf.sizeZ()/2, 'fourier') res = frm_fourier_adaptive_wedge_vol_rscore(vf2, wf, vg, wg, b, radius, weights) orientations = frm_find_topn_angles_interp(res) return position, orientations[0][0], orientations[0][1]