def julia_set_cp(zs, phase): ns = cp.zeros_like(Z, dtype=cp.float32) for i in range(n_iteration): # cupy doesn't support complex in where, we need to decompose it to real and img parts zs_real = cp.where( cp.abs(zs) < R, cp.real(zs**2 + 0.7885 * cp.exp(phase)), cp.real(zs)) zs_imag = cp.where( cp.abs(zs) < R, cp.imag(zs**2 + 0.7885 * cp.exp(phase)), cp.imag(zs)) zs = zs_real + 1j * zs_imag not_diverged = cp.abs(zs) < R ns = ns + not_diverged.astype(cp.float32) return ns, zs
def quantize(self, voltages, custom_stds=None): """ Quantize input complex voltages. Cache voltage means and standard deviations, per polarization and per antenna. Parameters ---------- voltages : array Array of complex voltages custom_stds : float, list, or array Custom standard deviation to use for scaling, instead of automatic calculation. Each quantizer will go from custom_stds values to self.target_std. Can either be a single value or an array-like object of length 2, to set the custom standard deviation for real and imaginary parts. Returns ------- q_voltages : array Array of complex quantized voltages """ try: assert len(custom_stds) == 2 except TypeError: custom_stds = [custom_stds] * 2 q_r = self.quantizer_r.quantize(xp.real(voltages), custom_std=custom_stds[0]) q_i = self.quantizer_i.quantize(xp.imag(voltages), custom_std=custom_stds[1]) self.stats_cache_r = self.quantizer_r.stats_cache self.stats_cache_i = self.quantizer_i.stats_cache return q_r + q_i * 1j
def cupy_signal(signal): amp = cp.sqrt(cp.real(signal * cp.conj(signal))) phase = cp.angle(signal) real = cp.real(signal) imag = cp.imag(signal) return amp, phase, real, imag
def positive_constrain(self,dn_3D,dF_3D_2): n_med = 1.334 wavelength = 532 k2 = (2*np.pi*n_med/wavelength)**2 n_med2 = n_med**2 # dF_3D = cupy.multiply(cupy.subtract(cupy.divide(cupy.multiply(dn_3D,dn_3D),n_med2),1),-k2) # dF_3D = cupy.fft.fftn(dF_3D) # dF_3D[cupy.not_equal(dF_3D_2,0)] = dF_3D_2[cupy.not_equal(dF_3D_2,0)] # dF_3D = cupy.fft.ifftn(dF_3D) # dn_3D = cupy.multiply(cupy.sqrt(cupy.add(cupy.divide(dF_3D,-k2), 1)), n_med) # dn_3D = cupy.fft.fftshift(dn_3D); dn_3D[cupy.less(cupy.real(dn_3D),n_med)] = n_med+1j*cupy.imag(dn_3D[cupy.less(cupy.real(dn_3D),n_med)]) dn_3D[cupy.less(cupy.imag(dn_3D),0)] = cupy.real(dn_3D[cupy.less(cupy.imag(dn_3D), 0)]) return dn_3D
def phase_optimization(filename_psf_target,scale=1,step=1.0,iterations=100,filename_phi="phases/foo.npy"): psf_target = np.load(filename_psf_target) print("Performing phase optimization...") if scale==1: Y = psf_target Y= cp.asarray(Y) else: dim_0 = scale*psf_target.shape[0] dim_1 = scale*psf_target.shape[1] dim = (dim_0,dim_1) Y= cv.resize(psf_target, dim, interpolation=cv.INTER_NEAREST) Y= cp.asarray(Y) if len(Y.shape)==2: Y = Y/cp.sum(Y) else: Y = Y/cp.reshape(cp.sum(Y,axis=(0,1)),(1,1)+Y.shape[2:]) phi = cp.random.random_sample(Y.shape)*2.0*np.pi #phi = cp.fft.fftshift(cp.angle(cp.fft.fft2(cp.sqrt(Y),axes=(0, 1))),axes=(0,1))+2.0*np.pi losses = [] #grads = [] mempool.free_all_blocks() for i in range(iterations): h = cp.fft.ifft2(cp.fft.ifftshift(cp.exp(1.0j*phi),axes=(0, 1)),axes=(0, 1)) psf_new = cp.square(cp.abs(h)) if len(psf_new.shape)==2: norm = cp.sum(psf_new) else: norm = cp.reshape(cp.sum(psf_new,axis=(0,1)),(1,1)+psf_new.shape[2:]) psf_new = psf_new/norm h = h/cp.sqrt(norm) D = (Y-psf_new) loss = cp.sum(cp.square(D)) losses.append(loss) if i%100==0: print("Iteration {} of {}, Loss: {}".format(i,iterations,loss)) gradient = -4.0*cp.imag(cp.exp(-1.0j*phi)*cp.fft.fftshift(cp.fft.fft2((D*h),axes=(0, 1)),axes=(0,1))) phi = phi - step*gradient #grads.append(cp.sum(gradient**2)) mempool.free_all_blocks() print("Phase Optimization process completed!") phi_np = cp.asnumpy(phi) np.save(filename_phi,phi_np)
def rf_c2r(rf: ndarrayA) -> ndarrayA: r"""Convert complex RF to real RF Usage: ``rf = rf_c2r(rf)`` Inputs: - ``rf``: `(N, 1, nT, (nCoils))`, RF pulse, complex Outputs: - ``rf``: `(N, xy, nT, (nCoils))`, RF pulse, x for real, y for imag. See Also: :func:`~mrphy.utils.rf_r2c` """ if isinstance(rf, ndarray_c): return np.concatenate((np.real(rf), np.imag(rf)), axis=1) else: # ndarray_g, i.e., cupy.ndarray return cp.concatenate((cp.real(rf), cp.imag(rf)), axis=1)
def quantize_complex(x, target_mean=0, target_std=32 / (2 * xp.sqrt(2 * xp.log(2))), num_bits=8, stats_calc_num_samples=10000): """ Quantize complex voltage data to integers with specified number of bits and target FWHM range. Parameters ---------- x : array Array of complex voltages target_mean : float, optional Target mean for voltages target_std : float, optional Target standard deviation for voltages num_bits : int, optional Number of bits to quantize to. Quantized voltages will span -2**(num_bits - 1) to 2**(num_bits - 1) - 1, inclusive. stats_calc_num_samples : int, optional Maximum number of samples for use in estimating noise statistics Returns ------- q_c : array Array of complex quantized voltages """ r, i = xp.real(x), xp.imag(x) q_r = quantize_real(r, target_mean=target_mean, target_std=target_std, num_bits=num_bits, stats_calc_num_samples=stats_calc_num_samples) q_i = quantize_real(i, target_mean=target_mean, target_std=target_std, num_bits=num_bits, stats_calc_num_samples=stats_calc_num_samples) q_c = q_r + q_i * 1j return q_c
def backward(self, grad_out): """ In the backward pass we receive a Tensor containing the gradient of the loss with respect to the output, and we need to compute the gradient of the loss with respect to the input. Thus, we need the gradient of the output with respect to the input, i.e. we need the gradient of the kernel tensor with respect to the phase profile. """ # grad_out: gradient of loss function (scalar) wrt kernels, thus, has same shape as kernel tensor grad_out = grad_out.numpy() #grad_out = cp.asarray(grad_out) phi = cp.asarray(self.phi) #First, we calculate the gradient of the loss function wrt the PSF #This will be in terms of the gradient of the loss function wrt the kernel tensor, grad_out def gradient_L_wrt_B(grad_out): grads_pos_pad = [np.pad(grad_out[:,:,:,i], ((pad,pad), (pad,pad),(0,0)), 'constant') for i in range(depth)] grads_pos = np.concatenate( [np.concatenate((grads_pos_pad[i*cols:(i+1)*cols]), axis=1) for i in range(rows)], axis=0) grads_neg_pad = [np.pad(-1.0*grad_out[:,:,:,i], ((pad,pad), (pad,pad),(0,0)), 'constant') for i in range(depth)] grads_neg = np.concatenate( [np.concatenate((grads_neg_pad[i*cols:(i+1)*cols]), axis=1) for i in range(rows)], axis=0) D = np.concatenate((grads_pos, grads_neg), axis=0) extra_pad_1 = (final_dim - np.shape(D)[0])//2 extra_pad_2 = (final_dim - np.shape(D)[1])//2 D = np.pad(D, ((extra_pad_1,extra_pad_1), (extra_pad_2,extra_pad_2),(0,0)), 'constant') dim = (final_dim*scale,final_dim*scale) D = cv.resize(D, dim, interpolation=cv.INTER_NEAREST) return D D = gradient_L_wrt_B(grad_out) A = np.load(filename_crosstalk) H = np.matmul(A.reshape((1,1)+A.shape),D.reshape(D.shape+(1,))).reshape(D.shape) H = cp.asarray(H) #Now, we can use this gradient to efficiently calculate the gradient of the loss wrt the phases h = cp.fft.ifft2(cp.fft.ifftshift(cp.exp(1.0j*phi),axes=(0, 1)),axes=(0, 1)) gradient_loss = 2.0*cp.imag(cp.exp(-1.0j*phi)*cp.fft.fftshift(cp.fft.fft2((H*h),axes=(0, 1)),axes=(0,1))) mempool.free_all_blocks() return gradient_loss*norm_factor
def mul_phase(magnitudes, phase): """ Parameters ------------ magnitudes: pytorch tensor phase: cupy ndarray """ phase_real = c2t(cp.real(phase)) phase_imag = c2t(cp.imag(phase)) input_real = magnitudes * phase_real input_imag = magnitudes * phase_imag # a virtual complex solution, since pytorch doesn't support complex type complex_spec = torch.cat( [input_real.unsqueeze(dim=-1), input_imag.unsqueeze(dim=-1)], dim=-1) return complex_spec
def normalize(fft_image): re, im = cp.real(fft_image), cp.imag(fft_image) length = cp.sqrt(re * re + im * im) return fft_image / length
def ampli_cupy(complex_arr): return((((cp.imag(complex_arr) ** 2) + (cp.real(complex_arr) ** 2)) ** 0.5))
def phase_cupy(complex_arr): return(cp.arctan(cp.imag(complex_arr)/cp.real(complex_arr)))
def calculate_correlations(record: OrderedDict, averaging_method: str) -> tuple: """ Calculates the auto- and cross-correlations for main and interferometer arrays given the bfiq data in record. Parameters ---------- record: OrderedDict hdf5 record containing bfiq data and metadata averaging_method: str Averaging method. Supported types are 'mean' and 'median' Returns ------- main_acfs: np.array Autocorrelation of the main array data intf_acfs: np.array Autocorrelation of the interferometer array data xcfs: np.array Cross-correlation of the main and interferometer arrays """ # TODO: Figure out how to remove pulse offsets pulse_phase_offset = record['pulse_phase_offset'] # bfiq data shape = [num_arrays, num_sequences, num_beams, num_samps] bfiq_data = record['data'] # Get the data and reshape num_arrays, num_sequences, num_beams, num_samps = record[ 'data_dimensions'] bfiq_data = bfiq_data.reshape(record['data_dimensions']) num_lags = len(record['lags']) main_corrs_unavg = xp.zeros( (num_sequences, num_beams, record['num_ranges'], num_lags), dtype=xp.complex64) intf_corrs_unavg = xp.zeros( (num_sequences, num_beams, record['num_ranges'], num_lags), dtype=xp.complex64) cross_corrs_unavg = xp.zeros( (num_sequences, num_beams, record['num_ranges'], num_lags), dtype=xp.complex64) # Loop through every sequence and compute correlations. # Output shape after loop is [num_sequences, num_beams, num_range_gates, num_lags] for sequence in range(num_sequences): # data input shape = [num_antenna_arrays, num_beams, num_samps] # data return shape = [num_beams, num_range_gates, num_lags] main_corrs_unavg[sequence, ...] = \ ProcessBfiq2Rawacf.correlations_from_samples(bfiq_data[0, sequence, :, :], bfiq_data[0, sequence, :, :], record) intf_corrs_unavg[sequence, ...] = \ ProcessBfiq2Rawacf.correlations_from_samples(bfiq_data[1, sequence, :, :], bfiq_data[1, sequence, :, :], record) cross_corrs_unavg[sequence, ...] = \ ProcessBfiq2Rawacf.correlations_from_samples(bfiq_data[1, sequence, :, :], bfiq_data[0, sequence, :, :], record) if averaging_method == 'median': main_corrs = xp.median( xp.real(main_corrs_unavg), axis=0) + 1j * xp.median(xp.imag(main_corrs_unavg), axis=0) intf_corrs = xp.median( xp.real(intf_corrs_unavg), axis=0) + 1j * xp.median(xp.imag(intf_corrs_unavg), axis=0) cross_corrs = xp.median( xp.real(cross_corrs_unavg), axis=0) + 1j * xp.median(xp.imag(cross_corrs_unavg), axis=0) else: # Using mean averaging main_corrs = xp.einsum('ijkl->jkl', main_corrs_unavg) / num_sequences intf_corrs = xp.einsum('ijkl->jkl', intf_corrs_unavg) / num_sequences cross_corrs = xp.einsum('ijkl->jkl', cross_corrs_unavg) / num_sequences main_acfs = main_corrs.flatten() intf_acfs = intf_corrs.flatten() xcfs = cross_corrs.flatten() return main_acfs, intf_acfs, xcfs
# cp_structure_factor.real[cp_amp%2==0]=cp_diff_amp[cp_amp%2==0] / cp.sqrt(2.0) # cp_structure_factor.imag[cp_amp%2==0]=cp_diff_amp[cp_amp%2==0] / cp.sqrt(2.0) # np_amp=cp.asnumpy(cp_amp) # tifffile.imsave(header + "_" + str(i+1) + '_np_amp.tif' ,np_amp) # cp_structure_factor_abs=cp.absolute(cp_structure_factor) # np_structure_factor=cp.asnumpy(cp_structure_factor_abs) # tifffile.imsave(header + "_" + str(i+1) + '_np_structure_factor_abs.tif' ,np_structure_factor) # cp_structure_factor = cp.fft.ifftshift(cp_structure_factor) cp_dens_pre = cp.fft.ifft2(cp_structure_factor, norm="ortho") cp_dens_pre_real = cp.real(cp_dens_pre) cp_dens_pre_imag = cp.imag(cp_dens_pre) cp_dens_pre_abs = cp.absolute(cp_dens_pre) #R-factorの計算 R_dens = cp_dens_pre * cp_sup R_structure_factor = cp.fft.fft2(R_dens, norm="ortho") R_structure_factor = cp.fft.fftshift(R_structure_factor) R_amp = cp.absolute(R_structure_factor) if (i + 1 == int(fft_iteration)): np_R_amp = cp.asnumpy(cp.square(R_amp)) #cupy配列 ⇒ numpy配列に変換 tifffile.imsave(header + "_" + str(i + 1).zfill(6) + '_diff.tif', np_R_amp) amp2_sum = cp.sum(cp.square(R_amp))
str(scale_factor[n]) + " " + str(R_factor[n]) + " " + str(OS_ratio[n]) + " " + str(gamma[n])) #実空間拘束 cp_dens_bk = cp.real(cp_dens) cp_dens.real[cp_sup % 2 == 1] = cp_dens_pre.real[cp_sup % 2 == 1] cp_dens.real[(cp_dens_pre.real % 2 < 0.0) | (cp_sup % 2 == 0)] = cp_dens_bk[ (cp_dens_pre.real % 2 < 0.0) | (cp_sup % 2 == 0)] - 0.9 * cp_dens_pre.real[ (cp_dens_pre.real % 2 < 0.0) | (cp_sup % 2 == 0)] if (complex_constraint_flag == 1): cp_dens_imag_bk = cp.imag(cp_dens) cp_dens.imag[cp_sup % 2 == 1] = cp_dens_pre.imag[cp_sup % 2 == 1] cp_dens.imag[(cp_dens_pre.imag % 2 < 0.0) | (cp_sup % 2 == 0)] = cp_dens_imag_bk[ (cp_dens_pre.imag % 2 < 0.0) | (cp_sup % 2 == 0)] - 0.9 * cp_dens_pre.imag[ (cp_dens_pre.imag % 2 < 0.0) | (cp_sup % 2 == 0)] else: cp_dens.imag[:, :, :] = 0.0 #OSS mask convolution if ((OSS_flag == 1) & ((i + 1) <= int(iteration))): cp_structure_factor = cp.fft.fftn(cp_dens, axes=(1, 2), norm="ortho") #【フーリエ変換】
def imag(arr): return cp.imag(arr)
def collect_data_block(self, digitize=True, requantize=True, verbose=True): """ General function to actually collect data from the antenna source and return coarsely channelized complex voltages. Collects one block of data. Parameters ---------- digitize : bool, optional Whether to quantize input voltages before the PFB requantize : bool, optional Whether to quantize output complex voltages after the PFB verbose : bool, optional Control whether tqdm prints progress messages Returns ------- final_voltages : array Complex voltages formatted according to GUPPI RAW specifications; array of shape (num_chans * num_antennas, block_size / (num_chans * num_antennas)) """ obsnchan = self.num_chans * self.num_antennas final_voltages = np.empty((obsnchan, int(self.block_size / obsnchan))) if self.input_file_stem is not None: if not requantize: raise ValueError( "Must set 'requantize=True' when using input RAW data!") input_voltages = self._read_next_block() # Make sure that block_size is appropriate assert self.block_size % int( obsnchan * self.num_taps * self.bytes_per_sample) == 0 T = int(self.block_size / (obsnchan * self.bytes_per_sample)) W = int(xp.ceil(T / self.num_taps / self.num_subblocks)) + 1 subblock_T = self.num_taps * (W - 1) # Change self.num_subblocks if necessary self.num_subblocks = int(xp.ceil(T / subblock_T)) subblock_t_len = int(subblock_T * self.bytes_per_sample) with tqdm(total=self.num_antennas * self.num_pols * self.num_subblocks, leave=False) as pbar: pbar.set_description('Subblocks') for subblock in range(self.num_subblocks): if verbose: tqdm.write(f'Creating subblock {subblock}...') # Change num windows at the end if self.num_subblocks doesn't go in evenly if T % subblock_T != 0 and subblock == self.num_subblocks - 1: W = int((T % subblock_T) / self.num_taps) + 1 if self.antenna_source.start_obs: num_samples = self.num_branches * self.num_taps * W else: num_samples = self.num_branches * self.num_taps * (W - 1) # Calculate the real voltage samples from each antenna t = time.time() antennas_v = self.antenna_source.get_samples(num_samples) self.sample_stage_t += time.time() - t for antenna in range(self.num_antennas): if verbose and self.is_antenna_array: tqdm.write(f'Creating antenna #{antenna}...') c_idx = antenna * self.num_chans + np.arange( 0, self.num_chans) for pol in range(self.num_pols): # Store indices used for numpy data i/o per polarization if T % subblock_T != 0 and subblock == self.num_subblocks - 1: # Uses smaller num windows W subblock_t_range = self.num_taps * ( W - 1) * self.bytes_per_sample else: subblock_t_range = subblock_t_len t_idx = subblock * subblock_t_len + self.num_bits // 4 * pol + np.arange( 0, subblock_t_range, self.num_bits // 4 * self.num_pols) # Send voltage data through the backend v = antennas_v[antenna][pol] if digitize: t = time.time() v = self.digitizer[antenna][pol].quantize(v) self.digitizer_stage_t += time.time() - t t = time.time() v = self.filterbank[antenna][pol].channelize(v) v = v[:, self.start_chan:self.start_chan + self.num_chans] self.filterbank_stage_t += time.time() - t if requantize: t = time.time() if self.input_file_stem is not None: temp_mean_r = self.requantizer[antenna][ pol].quantizer_r.target_mean self.requantizer[antenna][ pol].quantizer_r.target_mean = 0 temp_mean_i = self.requantizer[antenna][ pol].quantizer_i.target_mean self.requantizer[antenna][ pol].quantizer_i.target_mean = 0 # Start off assuming signals are embedded in Gaussian noise with std 1 custom_stds = self.filterbank[antenna][ pol].channelized_stds # If digitizing real voltages, scale up by the appropriate factor if digitize: custom_stds *= self.digitizer[antenna][ pol].target_std v = self.requantizer[antenna][pol].quantize( v, custom_stds=custom_stds) self.requantizer[antenna][ pol].quantizer_r.target_mean = temp_mean_r self.requantizer[antenna][ pol].quantizer_i.target_mean = temp_mean_i if self.num_bits == 8: input_v = input_voltages[c_idx[:, np.newaxis], (t_idx // 2 )[np.newaxis, :]] elif self.num_bits == 4: input_v = input_voltages[ c_idx[:, np.newaxis], t_idx[np.newaxis, :]] input_v = xp.array(input_v) v += input_v.T v = self.requantizer[antenna][pol].quantize(v) self.requantizer_stage_t += time.time() - t # Convert to numpy array if using cupy try: R = xp.asnumpy(xp.real(v).T) I = xp.asnumpy(xp.imag(v).T) except AttributeError: R = xp.real(v).T I = xp.imag(v).T if self.num_bits == 8 or not requantize: final_voltages[c_idx[:, np.newaxis], t_idx[np.newaxis, :]] = R final_voltages[c_idx[:, np.newaxis], (t_idx + 1)[np.newaxis, :]] = I elif self.num_bits == 4: # Translate 4 bit complex voltages to an 8 bit equivalent representation I[I < 0] += 16 final_voltages[c_idx[:, np.newaxis], t_idx[np.newaxis, :]] = R * 16 + I else: raise ValueError( f'{self.num_bits} bits not supported...') pbar.update(1) return final_voltages
#%% ORIGIN constrain and reconstuct # disp_3Dx = np.fft.fftshift(C_3D) # print(np.sum(np.isnan(F_3Dx))) plot_F_domain(np.fft.fftshift(F_3Dx), ffsize, df) plot_F_domain(np.fft.fftshift(C_3D/C_3D), ffsize, df) # dF_3Dx = cupy.fft.fftshift(dF_dst) dF_3Dx = cupy.array(F_3Dx.copy()) dF_3D = cupy.fft.ifftn(cupy.divide(dF_3Dx,dx)) dF_3D_2 = cupy.divide(dF_3Dx,dx) dn_3D = cupy.multiply(cupy.sqrt(cupy.add(cupy.divide(dF_3Dx,-k2), 1)), n_med) # Positive constraint dn_3D[cupy.less(cupy.real(dn_3D),n_med)] = cupy.real(n_med)+cupy.imag(dn_3D[cupy.less(cupy.real(dn_3D),n_med)]) dn_3D[cupy.less(cupy.imag(dn_3D),0)] = cupy.real(dn_3D[cupy.less(cupy.imag(dn_3D), 0)]) n_3D_bi = np.zeros(dn_3D.shape,dtype=int) # for iter in tqdm(range(0,100)): # dF_3D = cupy.multiply(cupy.subtract(cupy.divide(cupy.multiply(dn_3D,dn_3D),n_med2),1),-k2) # dF_3D = cupy.fft.fftn(dF_3D) # dF_3D[cupy.not_equal(dF_3D_2,0)] = dF_3D_2[cupy.not_equal(dF_3D_2,0)] # dF_3D = cupy.fft.ifftn(dF_3D) # dn_3D = cupy.multiply(cupy.sqrt(cupy.add(cupy.divide(dF_3D,-k2), 1)), n_med) # #Positive constraint # dn_3D[cupy.less(cupy.real(dn_3D),n_med)] = cupy.real(n_med)+cupy.imag(dn_3D[cupy.less(cupy.real(dn_3D),n_med)]) # dn_3D[cupy.less(cupy.imag(dn_3D),0)] = cupy.real(dn_3D[cupy.less(cupy.imag(dn_3D), 0)])
def phasecong100(im, nscale=2, norient=2, minWavelength=7, mult=2, sigmaOnf=0.65): rows, cols = im.shape imagefft = fft2(im) zero = cp.zeros(shape=(rows, cols)) EO = dict() EnergyV = cp.zeros((rows, cols, 3)) x_range = cp.linspace(-0.5, 0.5, num=cols, endpoint=True) y_range = cp.linspace(-0.5, 0.5, num=rows, endpoint=True) x, y = cp.meshgrid(x_range, y_range) radius = cp.sqrt(x**2 + y**2) theta = cp.arctan2(-y, x) radius = ifftshift(radius) theta = ifftshift(theta) radius[0, 0] = 1. sintheta = cp.sin(theta) costheta = cp.cos(theta) lp = lowpass_filter((rows, cols), 0.45, 15) logGabor = [] for s in range(1, nscale + 1): wavelength = minWavelength * mult**(s - 1.) fo = 1.0 / wavelength logGabor.append( cp.exp((-(cp.log(radius / fo))**2) / (2 * cp.log(sigmaOnf)**2))) logGabor[-1] *= lp logGabor[-1][0, 0] = 0 # The main loop... for o in range(1, norient + 1): angl = (o - 1.) * cp.pi / norient ds = sintheta * cp.cos(angl) - costheta * cp.sin(angl) dc = costheta * cp.cos(angl) + sintheta * cp.sin(angl) dtheta = cp.abs(cp.arctan2(ds, dc)) dtheta = cp.minimum(dtheta * norient / 2., cp.pi) spread = (cp.cos(dtheta) + 1.) / 2. sumE_ThisOrient = zero.copy() sumO_ThisOrient = zero.copy() for s in range(0, nscale): filter_ = logGabor[s] * spread EO[(s, o)] = ifft2(imagefft * filter_) sumE_ThisOrient = sumE_ThisOrient + cp.real(EO[(s, o)]) sumO_ThisOrient = sumO_ThisOrient + cp.imag(EO[(s, o)]) EnergyV[:, :, 0] = EnergyV[:, :, 0] + sumE_ThisOrient EnergyV[:, :, 1] = EnergyV[:, :, 1] + cp.cos(angl) * sumO_ThisOrient EnergyV[:, :, 2] = EnergyV[:, :, 2] + cp.sin(angl) * sumO_ThisOrient OddV = cp.sqrt(EnergyV[:, :, 0]**2 + EnergyV[:, :, 1]**2) featType = cp.arctan2(EnergyV[:, :, 0], OddV) return featType