def create_sphere(size, radius=-1, sigma=0, num_sigma=2, center=None, gpu=False): """Create a 3D sphere volume. @param size: size of the resulting volume. @param radius: radius of the sphere inside the volume. @param sigma: sigma of the Gaussian. @param center: center of the sphere. @return: sphere inside a volume. """ if size.__class__ == float or len(size) == 1: size = (size, size, size) assert len(size) == 3 if center is None: center = [size[0] // 2, size[1] // 2, size[2] // 2] if radius == -1: radius = xp.min(size) // 2 sphere = xp.zeros(size, dtype=xp.float32) [x, y, z] = xp.mgrid[0:size[0], 0:size[1], 0:size[2]] r = xp.sqrt((x - center[0])**2 + (y - center[1])**2 + (z - center[2])**2) sphere[r <= radius] = 1 if sigma > 0: ind = xp.logical_and(r > radius, r < radius + num_sigma * sigma) sphere[ind] = xp.exp(-((r[ind] - radius) / sigma)**2 / 2) return sphere
def add_noise(data, snr=0.1, m=0): """Add gaussian noise to the given volume. @param data: input volume. @param snr: SNR of the added noise. @param m: mean of the added noise. @return The image with gaussian noise """ vs = xp.var(data) vn = vs / snr sd = xp.sqrt(vn) s = xp.ndarray(data.shape) noise = sd * standard_normal(s.shape) + m t = data + noise return t
def circle_filter(sizeX, sizeY, radiusCutoff): """ circleFilter: NEEDS Documentation @param sizeX: NEEDS Documentation @param sizeY: NEEDS Documentation @param radiusCutoff: NEEDS Documentation """ X, Y = xp.meshgrid( xp.arange(-sizeX // 2 + sizeX % 2, sizeX // 2 + sizeX % 2), xp.arange(-sizeY // 2 + sizeY % 2, sizeY // 2 + sizeY % 2)) R = xp.sqrt(X**2 + Y**2) filter = xp.zeros((sizeX, sizeY), dtype=xp.float32) filter[R <= radiusCutoff] = 1 return filter
def ellipse_filter(sizeX, sizeY, radiusCutoffX, radiusCutoffY): """ circleFilter: NEEDS Documentation @param sizeX: NEEDS Documentation @param sizeY: NEEDS Documentation @param radiusCutoff: NEEDS Documentation """ X, Y = xp.meshgrid( xp.arange(-sizeY // 2 + sizeY % 2, sizeY // 2 + sizeY % 2), xp.arange(-sizeX // 2 + sizeX % 2, sizeX // 2 + sizeX % 2)) R = xp.sqrt((X / radiusCutoffX)**2 + (Y / radiusCutoffY)**2) filter = xp.zeros((sizeX, sizeY), dtype=xp.float32) #print(filter.shape, R.shape) filter[R <= 1] = 1 return filter
def invert_WedgeSum(invol, r_max=None, lowlimit=0., lowval=0.): """ invert wedge sum - avoid division by zero and boost of high frequencies @param invol: input volume @type invol: L{pytom_volume.vol} or L{pytom_volume.vol_comp} @param r_max: radius @type r_max: L{int} @param lowlimit: lower limit - all values below this value that lie in the specified radius will be replaced \ by lowval @type lowlimit: L{float} @param lowval: replacement value @type lowval: L{float} @author: FF """ from math import sqrt if not r_max: r_max = invol.shape[1] // 2 - 1 dx, dy, dz = invol.shape if dz != dx: X, Y, Z = xp.meshgrid(xp.arange(-dx // 2, dx // 2 + dx % 2), xp.arange(-dy // 2, dy // 2 + dy % 2), xp.arange(0, dz)) invol = xp.fft.fftshift(invol, axes=(0, 1)) else: X, Y, Z = xp.meshgrid(xp.arange(-dx // 2, dx // 2 + dx % 2), xp.arange(-dy // 2, dy // 2 + dy % 2), xp.arange(-dz // 2, dz // 2 + dz % 2)) R = xp.sqrt(X**2 + Y**2 + Z**2).astype(xp.int32) invol_out = invol.copy().astype(xp.float32) invol_out[invol < lowlimit] = lowval invol_out = 1. / invol_out invol_out[R >= r_max] = 0 if dx != dz: invol_out = xp.fft.fftshift(invol_out, axes=(0, 1)) return invol_out
def norm_inside_mask(inp, mask): """ To normalise a 2D array within a mask @param inp: A 2D array to be normalized. @type inp: numpy array 2D @param mask: A 2D array of the same size as the input to mask of parts of the ixp. @type mask: numpy array 2D @return: A normalized 2D array. @returntype: numpy array 2D @requires: the shape of inp to be equal to the shape of the mask """ # assert ixp.shape == mask.shape # assert len(ixp.shape) == 2 mea = xp.divide(xp.sum(xp.multiply(inp, mask)), xp.sum(mask)) st = xp.sqrt( xp.sum((xp.multiply(mask, mea) + xp.multiply(inp, mask) - mea)**2) / xp.sum(mask)) return xp.multiply((inp - mea) / st, mask)
def profile2FourierVol(profile, dim=None, reduced=False): """ create Volume from 1d radial profile, e.g., to modulate signal with \ specific function such as CTF or FSC. Simple linear interpolation is used\ for sampling. @param profile: profile @type profile: 1-d L{pytom_volume.vol} or 1-d python array @param dim: dimension of (cubic) output @type dim: L{int} @param reduced: If true reduced Fourier representation (N/2+1, N, N) is generated. @type reduced: L{bool} @return: 3-dim complex volume with spherically symmetrical profile @rtype: L{pytom_volume.vol} @author: FF """ if dim is None: try: dim = [ 2 * profile.shape[0], ] * 3 except: dim = [ 2 * len(profile), ] * 3 is3D = (len(dim) == 3) nx, ny = dim[:2] if reduced: if is3D: nz = int(dim[2] // 2) + 1 else: ny = int(ny // 2) + 1 else: if is3D: nz = dim[2] try: r_max = profile.shape[0] - 1 except: r_max = len(profile) - 1 if len(dim) == 3: if reduced: X, Y, Z = xp.meshgrid(xp.arange(-nx // 2, nx // 2 + nx % 2), xp.arange(-ny // 2, ny // 2 + ny % 2), xp.arange(0, nz)) else: X, Y, Z = xp.meshgrid(xp.arange(-nx // 2, nx // 2 + nx % 2), xp.arange(-ny // 2, ny // 2 + ny % 2), xp.arange(-nz // 2, nz // 2 + nz % 2)) R = xp.sqrt(X**2 + Y**2 + Z**2) else: if reduced: X, Y = xp.meshgrid(xp.arange(-nx // 2, ny // 2 + ny % 2), xp.arange(0, ny)) else: X, Y = xp.meshgrid(xp.arange(-nx // 2, nx // 2 + nx % 2), xp.arange(-ny // 2, ny // 2 + ny % 2)) R = xp.sqrt(X**2 + Y**2) IR = xp.floor(R).astype(xp.int64) valIR_l1 = IR.copy() valIR_l2 = valIR_l1 + 1 val_l1, val_l2 = xp.zeros_like(X, dtype=xp.float64), xp.zeros_like( X, dtype=xp.float64) l1 = R - IR.astype(xp.float32) l2 = 1 - l1 try: profile = xp.array(profile) except: import numpy profile = xp.array(numpy.array(profile)) for n in xp.arange(r_max): val_l1[valIR_l1 == n] = profile[n] val_l2[valIR_l2 == n + 1] = profile[n + 1] val_l1[IR == r_max] = profile[n + 1] val_l2[IR == r_max] = profile[n + 1] val_l1[R > r_max] = 0 val_l2[R > r_max] = 0 fkernel = l2 * val_l1 + l1 * val_l2 if reduced: fkernel = xp.fft.fftshift(fkernel, axes=(0, 1)) else: fkernel = xp.fft.fftshift(fkernel) return fkernel
def create_asymmetric_wedge(angle1, angle2, cutoffRadius, sizeX, sizeY, sizeZ, smooth, rotation=None): '''This function returns an asymmetric wedge object. @param angle1: angle of wedge1 in degrees @type angle1: int @param angle2: angle of wedge2 in degrees @type angle2: int @param cutOffRadius: radius from center beyond which the wedge is set to zero. @type cutOffRadius: int @param sizeX: the size of the box in x-direction. @type sizeX: int @param sizeY: the size of the box in y-direction. @type sizeY: int @param sizeZ: the size of the box in z-direction. @type sizeZ: int @param smooth: smoothing parameter that defines the amount of smoothing at the edge of the wedge. @type smooth: float @return: 3D array determining the wedge object. @rtype: ndarray of xp.float64''' range_angle1Smooth = smooth / xp.sin(angle1 * xp.pi / 180.) range_angle2Smooth = smooth / xp.sin(angle2 * xp.pi / 180.) wedge = xp.zeros((sizeX, sizeY, sizeZ // 2 + 1)) if rotation is None: z, y, x = xp.meshgrid( xp.arange(-sizeX // 2 + sizeX % 2, sizeX // 2 + sizeX % 2), xp.arange(-sizeY // 2 + sizeY % 2, sizeY // 2 + sizeY % 2), xp.arange(0, sizeZ // 2 + 1)) else: cx, cy, cz = [s // 2 for s in (sizeX, sizeY, sizeZ)] grid = xp.mgrid[-cx:sizeX - cx, -cy:sizeY - cy, :sizeZ // 2 + 1] phi, the, psi = rotation phi = -float(phi) * xp.pi / 180.0 the = -float(the) * xp.pi / 180.0 psi = -float(psi) * xp.pi / 180.0 sin_alpha = xp.sin(phi) cos_alpha = xp.cos(phi) sin_beta = xp.sin(the) cos_beta = xp.cos(the) sin_gamma = xp.sin(psi) cos_gamma = xp.cos(psi) # Calculate inverse rotation matrix Inv_R = xp.zeros((3, 3), dtype='float32') Inv_R[0, 0] = cos_alpha * cos_gamma - cos_beta * sin_alpha \ * sin_gamma Inv_R[0, 1] = -cos_alpha * sin_gamma - cos_beta * sin_alpha \ * cos_gamma Inv_R[0, 2] = sin_beta * sin_alpha Inv_R[1, 0] = sin_alpha * cos_gamma + cos_beta * cos_alpha \ * sin_gamma Inv_R[1, 1] = -sin_alpha * sin_gamma + cos_beta * cos_alpha \ * cos_gamma Inv_R[1, 2] = -sin_beta * cos_alpha Inv_R[2, 0] = sin_beta * sin_gamma Inv_R[2, 1] = sin_beta * cos_gamma Inv_R[2, 2] = cos_beta temp = grid.reshape((3, grid.size // 3)) temp = xp.dot(Inv_R, temp) grid = xp.reshape(temp, grid.shape) y = grid[0, :, :, :] z = grid[1, :, :, :] x = grid[2, :, :, :] r = xp.sqrt(x**2 + y**2 + z**2) wedge[xp.tan(angle1 * xp.pi / 180) < y / x] = 1 wedge[xp.tan(-angle2 * xp.pi / 180) > y / x] = 1 wedge[sizeX // 2, :, 0] = 1 if smooth: area = xp.abs(x - (y / xp.tan(angle1 * xp.pi / 180))) <= range_angle1Smooth strip = 1 - (xp.abs(x - (y / xp.tan(angle1 * xp.pi / 180.))) * xp.sin(angle1 * xp.pi / 180.) / smooth) wedge += (strip * area * (1 - wedge) * (y > 0)) area2 = xp.abs(x + (y / xp.tan(angle2 * xp.pi / 180))) <= range_angle2Smooth strip2 = 1 - (xp.abs(x + (y / xp.tan(angle2 * xp.pi / 180.))) * xp.sin(angle2 * xp.pi / 180.) / smooth) wedge += (strip2 * area2 * (1 - wedge) * (y <= 0)) wedge[r > cutoffRadius] = 0 return xp.fft.fftshift(wedge, axes=(0, 1))
def weightedXCF(volume, reference, numberOfBands, wedgeAngle=-1, gpu=False): """ weightedXCF: Determines the weighted correlation function for volume and reference @param volume: A volume @param reference: A reference @param numberOfBands:Number of bands @param wedgeAngle: A optional wedge angle @return: The weighted correlation function @rtype: L{pytom_volume.vol} @author: Thomas Hrabe @todo: does not work yet -> test is disabled """ if gpu: import cupy as xp else: import numpy as xp from pytom.tompy.correlation import bandCF from pytom.tompy.transforms import fourier_reduced2full from math import sqrt import pytom_freqweight result = xp.zeros_like(volume) q = 0 if wedgeAngle >= 0: wedgeFilter = pytom_freqweight.weight(wedgeAngle, 0, volume.sizeX(), volume.sizeY(), volume.sizeZ()) wedgeVolume = wedgeFilter.getWeightVolume(True) else: wedgeVolume = xp.ones_like(volume) w = xp.sqrt(1 / float(volume.size)) numberVoxels = 0 for i in range(numberOfBands): """ notation according Steward/Grigorieff paper """ band = [0, 0] band[0] = i * volume.shape[0] / numberOfBands band[1] = (i + 1) * volume.shape[0] / numberOfBands r = bandCF(volume, reference, band, gpu=gpu) cc = r[0] filter = r[1] #get bandVolume bandVolume = filter.getWeightVolume(True) filterVolumeReduced = bandVolume * wedgeVolume filterVolume = fourier_reduced2full(filterVolumeReduced) #determine number of voxels != 0 N = filterVolume[abs(filterVolume) < 1].sum() #add to number of total voxels numberVoxels = numberVoxels + N cc2 = r[0].copy() cc2 = cc2**2 cc = shiftscale(cc, w, 1) ccdiv = cc2 / (cc) ccdiv = ccdiv**3 #abs(ccdiv); as suggested by grigorief ccdiv = shiftscale(ccdiv, 0, N) result = result + ccdiv result = shiftscale(result, 0, 1 / float(numberVoxels)) return result
def weightedXCC(volume, reference, numberOfBands, wedgeAngle=-1, gpu=False): """ weightedXCC: Determines the band weighted correlation coefficient for a volume and reference. Notation according Steward/Grigorieff paper @param volume: A volume @type volume: L{pytom_volume.vol} @param reference: A reference of same size as volume @type reference: L{pytom_volume.vol} @param numberOfBands: Number of bands @param wedgeAngle: A optional wedge angle @return: The weighted correlation coefficient @rtype: float @author: Thomas Hrabe """ if gpu: import cupy as xp else: import numpy as xp from pytom.tompy.transform import fft, fourier_reduced2full from pytom.basic.structures import WedgeInfo result = 0 numberVoxels = 0 #volume.write('vol.em'); #reference.write('ref.em'); wedge = WedgeInfo(wedgeAngle) wedgeVolume = wedge.returnWedgeVolume(volume.shape[0], volume.shape[1], volume.shape[2]) increment = int(volume.shape[0] / 2 * 1 / numberOfBands) band = [0, 100] for i in range(0, volume.shape[0] / 2, increment): band[0] = i band[1] = i + increment r = bandCC(volume, reference, band, gpu=gpu) cc = r[0] #print cc; filter = r[1] #get bandVolume bandVolume = filter.getWeightVolume(True) filterVolumeReduced = bandVolume * wedgeVolume filterVolume = fourier_reduced2full(filterVolumeReduced) #determine number of voxels != 0 N = (filterVolume != 0).sum() w = xp.sqrt(1 / float(N)) #add to number of total voxels numberVoxels = numberVoxels + N #print 'w',w; #print 'cc',cc; #print 'N',N; cc2 = cc * cc #print 'cc2',cc2; if cc <= 0.0: cc = cc2 else: cc = cc2 / (cc + w) #print 'cc',cc; cc = cc * cc * cc #no abs #print 'cc',cc; #add up result result = result + cc * N return result * (1 / float(numberVoxels))
def bandCC(volume, reference, band, verbose=False, shared=None, index=None): """ bandCC: Determines the normalised correlation coefficient within a band @param volume: The volume @type volume: L{pytom_volume.vol} @param reference: The reference @type reference: L{pytom_volume.vol} @param band: [a,b] - specify the lower and upper end of band. @return: First parameter - The correlation of the two volumes in the specified band. Second parameter - The bandpass filter used. @rtype: List - [float,L{pytom_freqweight.weight}] @author: Thomas Hrabe """ if not index is None: print(index) from pytom.tompy.filter import bandpass from pytom.tompy.correlation import xcf #from pytom.tompy.filter import vol_comp if verbose: print('lowest freq : ', band[0], ' highest freq', band[1]) vf, m = bandpass(volume, band[0], band[1], returnMask=True, fourierOnly=True) rf = bandpass(reference, band[0], band[1], mask=m, fourierOnly=True) #,vf[1]) #ccVolume = vol_comp(rf[0].shape[0],rf[0].shape[1],rf[0].shape[2]) #ccVolume.copyVolume(rf[0]) vf = vf.astype(xp.complex128) ccVolume = rf.astype(vf.dtype) ccVolume = ccVolume * xp.conj(vf) #pytom_volume.conj_mult(ccVolume,vf[0]) cc = ccVolume.sum() cc = cc.real v = vf r = rf absV = xp.abs(v) absR = xp.abs(r) sumV = xp.sum(absV**2) sumR = xp.sum(absR**2) sumV = xp.abs(sumV) sumR = xp.abs(sumR) if sumV == 0: sumV = 1 if sumR == 0: sumR = 1 cc = cc / (xp.sqrt(sumV * sumR)) #numerical errors will be punished with nan if abs(cc) > 1.1: cc = float('nan') return float(cc)
def randomizePhaseBeyondFreq(volume, frequency): '''This function randomizes the phases beyond a given frequency, while preserving the Friedel symmetry. @param volume: target volume @type volume: 3d ndarray @param frequency: frequency in pixel beyond which phases are randomized. @type frequency: int @return: 3d volume. @rtype: 3d ndarray @author: GvdS''' from numpy.fft import ifftn, fftshift, fftn, rfftn, irfftn from numpy import angle, abs, exp, meshgrid, arange, sqrt, pi, rot90 threeD = len(volume.shape) == 3 twoD = len(volume.shape) == 2 if not twoD and not threeD: raise Exception( 'Invalid volume dimensions: either supply 3D or 2D ndarray') if twoD: dx, dy = volume.shape else: dx, dy, dz = volume.shape ft = xp.fft.rfftn(volume) phase = xp.angle(ft) amplitude = xp.abs(ft) rnda = generate_random_phases_3d( amplitude.shape, reduced_complex=True) # (ranf((dx,dy,dz//2+1)) * pi * 2) - pi if twoD: X, Y = xp.meshgrid(xp.arange(-dx // 2, dx // 2 + dx % 2), xp.arange(-dy // 2, dy // 2 + dy % 2)) RF = xp.sqrt(X**2 + Y**2).astype(int) R = xp.fft.fftshift(RF)[:, :dy // 2 + 1] else: X, Y, Z = xp.meshgrid(xp.arange(-dx // 2, dx // 2), xp.arange(-dy // 2, dy // 2), xp.arange(-dz // 2, dz // 2)) RF = xp.sqrt(X**2 + Y**2 + Z**2) # .astype(int) R = xp.fft.fftshift(RF)[:, :, :dz // 2 + 1] # centralslice = fftshift(rnda[:,:,0]) # cc = dx//2 # ccc= (dx-1)//2 # centralslice[cc, cc-ccc:cc] = centralslice[cc,-ccc:][::-1]*-1 # centralslice[cc-ccc:cc,cc-ccc:] = rot90(centralslice[-ccc:,cc-ccc:],2)*-1 # rnda[:,:,0] = fftshift(centralslice) rnda[R <= frequency] = 0 phase[R > frequency] = 0 phase += rnda image = xp.fft.irfftn((amplitude * xp.exp(1j * phase)), s=volume.shape) if xp.abs(image.imag).sum() > 1E-8: raise Exception( 'Imaginary part is non-zero. Failed to centro-summetrize the phases.' ) return image.real