def reset(self): """ Reset state variables. Necesary after changing or setting the filter or zero padding. """ self.num_frames = 0 self.nbin = self.nfft // 2 + 1 self.freq = np.linspace(0,self.fs/2,self.nbin) if self.D==1: self.fft_in_buffer[:] = 0. self.X[:] = 0. self.y_p[:] = 0. else: self.fft_in_buffer[:,:] = 0. self.X[:,:] = 0. self.y_p[:,:] = 0. self.dft = DFT(nfft=self.nfft,fs=self.fs,num_sig=self.D, analysis_window=self.analysis_window, synthesis_window=self.synthesis_window, transform=self.transform)
def computeFFT(self, imge): """Computes the FFT of a given image. """ #Compute size of the given image N = imge.shape[0] #Compute the FFT for the base case (which uses the normal DFT) if N == 2: return DFT.computeForward2DDFTNoSeparability(imge) #Otherwise compute FFT recursively #Divide the original image into even and odd imgeEE = np.array([[imge[i, j] for i in xrange(0, N, 2)] for j in xrange(0, N, 2)]).T imgeEO = np.array([[imge[i, j] for i in xrange(0, N, 2)] for j in xrange(1, N, 2)]).T imgeOE = np.array([[imge[i, j] for i in xrange(1, N, 2)] for j in xrange(0, N, 2)]).T imgeOO = np.array([[imge[i, j] for i in xrange(1, N, 2)] for j in xrange(1, N, 2)]).T #Compute FFT for each of the above divided images FeeUV = FFT.computeFFT(imgeEE) FeoUV = FFT.computeFFT(imgeEO) FoeUV = FFT.computeFFT(imgeOE) FooUV = FFT.computeFFT(imgeOO) #Compute also Ws Wu = FFT.__computeW(N / 2, N) Wv = Wu.T #Transpose Wuv = FFT.__computeW(N / 2, N, oneD=False) #Compute F(u,v) for u,v = 0,1,2,...,N/2 imgeFuv = 0.25 * (FeeUV + (FeoUV * Wv) + (FoeUV * Wu) + (FooUV * Wuv)) #Compute F(u, v+M) where M = N/2 imgeFuMv = 0.25 * (FeeUV + (FeoUV * Wv) - (FoeUV * Wu) - (FooUV * Wuv)) #Compute F(u+M, v) where M = N/2 imgeFuvM = 0.25 * (FeeUV - (FeoUV * Wv) + (FoeUV * Wu) - (FooUV * Wuv)) #Compute F(u+M, v+M) where M = N/2 imgeFuMvM = 0.25 * (FeeUV - (FeoUV * Wv) - (FoeUV * Wu) + (FooUV * Wuv)) imgeF1 = np.hstack((imgeFuv, imgeFuvM)) imgeF2 = np.hstack((imgeFuMv, imgeFuMvM)) imgeFFT = np.vstack((imgeF1, imgeF2)) return imgeFFT
def compress_image(im_fft, compression_level, originalCount): if compression_level < 0 or compression_level > 100: AssertionError('compression_level must be between 0 to 100') rest = 100 - compression_level lower = np.percentile(im_fft, rest // 2) upper = np.percentile(im_fft, 100 - rest // 2) print('non zero values for level {}% are {} out of {}'.format( compression_level, int(originalCount * ((100 - compression_level) / 100.0)), originalCount)) compressed_im_fft = im_fft * \ np.logical_or(im_fft <= lower, im_fft >= upper) save_npz('coefficients-{}-compression.csr'.format(compression_level), csr_matrix(compressed_im_fft)) return DFT.fast_two_dimension_inverse(compressed_im_fft)
def __init__(self, N, fs, hop=None, analysis_window=None, synthesis_window=None, channels=1, transform='numpy'): """ Constructor for STFT class. Parameters ----------- N : int number of samples per frame fs : float Sampling frequency. hop : int hop size wA : numpy array analysis window wS : numpy array synthesis window channels : int number of signals """ # initialize parameters self.N = N # number of samples per frame self.fs = fs self.D = channels # number of channels if hop is not None: # hop size self.hop = hop else: self.hop = self.N/2 self.hop = int(np.floor(self.hop)) # analysis window if analysis_window is not None: self.analysis_window = analysis_window elif analysis_window is None and self.hop ==self.N/2: self.analysis_window = windows.hann(self.N) else: self.analysis_window = None # synthesis window if synthesis_window is not None: self.synthesis_window = synthesis_window elif synthesis_window is None and self.hop ==self.N/2: self.synthesis_window = None # rectangular window else: self.synthesis_window = None # create DFT object self.transform = transform self.nfft = self.N self.nbin = self.nfft // 2 + 1 self.freq = np.linspace(0,self.fs/2,self.nbin) self.dft = DFT(nfft=self.nfft,fs=self.fs,num_sig=self.D, analysis_window=self.analysis_window, synthesis_window=self.synthesis_window, transform=self.transform) self.fft_out_buffer = np.zeros(self.nbin, dtype=np.complex64) # initialize filter + zero padding --> use set_filter self.zf = 0; self.zb = 0 self.H = None # filter frequency spectrum # state variables self.num_frames = 0 # number of frames processed so far self.n_state = self.N - self.hop # allocate all the required buffers self.make_buffers()
class STFT: """ Methods -------- analysis(x_n) Perform STFT on most recent samples. process(h) Perform filtering in frequency domain. synthesis() Transform to time domain and use overlap-and-add to reconstruct the output. set_filter(h, zb, zf) Set time-domain filter with appropriate zero-padding. get_prev_samples() Get previous reconstructed samples. zero_pad_front(zf) Set zero-padding at beginning of frame. zero_pad_back(zb) Set zero-padding at end of frame. reset() Reset state variables. Necessary after changing or setting the filter. visualize_frame(fmin=None,fmax=None,plot_time=False) Visualize frequency spectrum of current frame. spectogram(x, fmin=None,fmax=None,tmin=None,tmax=None,plot_time=False) Plot spectrogram according to object's parameters and given signal. """ def __init__(self, N, fs, hop=None, analysis_window=None, synthesis_window=None, channels=1, transform='numpy'): """ Constructor for STFT class. Parameters ----------- N : int number of samples per frame fs : float Sampling frequency. hop : int hop size wA : numpy array analysis window wS : numpy array synthesis window channels : int number of signals """ # initialize parameters self.N = N # number of samples per frame self.fs = fs self.D = channels # number of channels if hop is not None: # hop size self.hop = hop else: self.hop = self.N/2 self.hop = int(np.floor(self.hop)) # analysis window if analysis_window is not None: self.analysis_window = analysis_window elif analysis_window is None and self.hop ==self.N/2: self.analysis_window = windows.hann(self.N) else: self.analysis_window = None # synthesis window if synthesis_window is not None: self.synthesis_window = synthesis_window elif synthesis_window is None and self.hop ==self.N/2: self.synthesis_window = None # rectangular window else: self.synthesis_window = None # create DFT object self.transform = transform self.nfft = self.N self.nbin = self.nfft // 2 + 1 self.freq = np.linspace(0,self.fs/2,self.nbin) self.dft = DFT(nfft=self.nfft,fs=self.fs,num_sig=self.D, analysis_window=self.analysis_window, synthesis_window=self.synthesis_window, transform=self.transform) self.fft_out_buffer = np.zeros(self.nbin, dtype=np.complex64) # initialize filter + zero padding --> use set_filter self.zf = 0; self.zb = 0 self.H = None # filter frequency spectrum # state variables self.num_frames = 0 # number of frames processed so far self.n_state = self.N - self.hop # allocate all the required buffers self.make_buffers() # def make_buffers(self): # # The input buffer, float32 for speed! # self.fft_in_buffer = np.zeros((self.nfft, self.D), dtype=np.float32) # # a number of useful views on the input buffer # self.x_p = self.fft_in_buffer[self.zf:self.zf+self.n_state,:] # State buffer # self.fresh_samples = self.fft_in_buffer[self.zf+self.n_state:self.zf+self.n_state+self.hop,:] # self.old_samples = self.fft_in_buffer[self.zf+self.hop:self.zf+self.hop+self.n_state,:] # self.y_p = np.zeros((self.zb, self.D), dtype=np.float32).squeeze() # prev reconstructed samples # self.X = np.zeros((self.nbin, self.D), dtype=np.complex64) # current frame in STFT domain def make_buffers(self): if self.D==1: # need this distinction for fftw # The input buffer, float32 for speed! self.fft_in_buffer = np.zeros(self.nfft, dtype=np.float32) # a number of useful views on the input buffer self.x_p = self.fft_in_buffer[self.zf:self.zf+self.n_state] # State buffer self.fresh_samples = self.fft_in_buffer[self.zf+self.n_state:self.zf+self.n_state+self.hop] self.old_samples = self.fft_in_buffer[self.zf+self.hop:self.zf+self.hop+self.n_state] self.y_p = np.zeros(self.zb, dtype=np.float32) # prev reconstructed samples self.X = np.zeros(self.nbin, dtype=np.complex64) # current frame in STFT domain self.out = np.zeros(self.hop, dtype=np.float32) else: # The input buffer, float32 for speed! self.fft_in_buffer = np.zeros((self.nfft, self.D), dtype=np.float32) # a number of useful views on the input buffer self.x_p = self.fft_in_buffer[self.zf:self.zf+self.n_state,:] # State buffer self.fresh_samples = self.fft_in_buffer[self.zf+self.n_state:self.zf+self.n_state+self.hop,:] self.old_samples = self.fft_in_buffer[self.zf+self.hop:self.zf+self.hop+self.n_state,:] self.y_p = np.zeros((self.zb, self.D), dtype=np.float32) # prev reconstructed samples self.X = np.zeros((self.nbin, self.D), dtype=np.complex64) # current frame in STFT domain self.out = np.zeros((self.hop,self.D), dtype=np.float32) def reset(self): """ Reset state variables. Necesary after changing or setting the filter or zero padding. """ self.num_frames = 0 self.nbin = self.nfft // 2 + 1 self.freq = np.linspace(0,self.fs/2,self.nbin) if self.D==1: self.fft_in_buffer[:] = 0. self.X[:] = 0. self.y_p[:] = 0. else: self.fft_in_buffer[:,:] = 0. self.X[:,:] = 0. self.y_p[:,:] = 0. self.dft = DFT(nfft=self.nfft,fs=self.fs,num_sig=self.D, analysis_window=self.analysis_window, synthesis_window=self.synthesis_window, transform=self.transform) def zero_pad_front(self, zf): """ Set zero-padding at beginning of frame. """ self.zf = zf self.nfft = self.N+self.zb+self.zf if self.analysis_window is not None: self.analysis_window = np.concatenate((np.zeros(zf), self.analysis_window)) if self.synthesis_window is not None: self.synthesis_window = np.concatenate((np.zeros(zf), self.synthesis_window)) def zero_pad_back(self, zb): """ Set zero-padding at end of frame. """ self.zb = zb self.nfft = self.N+self.zb+self.zf if self.analysis_window is not None: self.analysis_window = np.concatenate((self.analysis_window, np.zeros(zb))) if self.synthesis_window is not None: self.synthesis_window = np.concatenate((self.synthesis_window, np.zeros(zb))) def set_filter(self, coeff, zb=None, zf=None, freq=False): """ Set time-domain filter with appropriate zero-padding. Frequency spectrum of the filter is computed and set for the object. There is also a check for sufficient zero-padding. Parameters ----------- coeff : numpy array Filter in time domain. zb : int Amount of zero-padding added to back/end of frame. zf : int Amount of zero-padding added to front/beginning of frame. """ # apply zero-padding if zb is not None: self.zero_pad_back(zb) if zf is not None: self.zero_pad_front(zf) self.reset() if not freq: # compute filter magnitude and phase spectrum self.H = np.complex64(np.fft.rfft(coeff, self.nfft, axis=0)) # check for sufficient zero-padding if self.nfft < (self.N+len(coeff)-1): raise ValueError('Insufficient zero-padding for chosen number of samples per frame (L) and filter length (h). Require zero-padding such that new length is at least (L+h-1).') else: if len(coeff)!=self.nbin: raise ValueError('Invalid length for frequency domain coefficients.') self.H = coeff # We need to reallocate buffers after changing zero padding self.make_buffers() def analysis(self, x_n): """ Transform new samples to STFT domain for analysis. Parameters ----------- x_n : numpy array [self.hop] new samples. Returns ----------- self.X : numpy array Frequency spectrum of given frame. """ # check for valid input - already done by self.dft # if x_n.shape[0]!=self.hop: # raise ValueError('Invalid input dimensions.') # if self.D > 1 and x_n.shape[1]!=self.D: # raise ValueError('Invalid input dimensions.') if x_n.ndim == 1: self.fresh_samples[:] = x_n[:] else: self.fresh_samples[:,:] = x_n[:,:] # apply DFT to current frame self.X[:] = self.dft.analysis(self.fft_in_buffer) self.x_p[:] = self.old_samples # self.num_frames += 1 def process(self): """ Apply filtering in STFT domain. Returns ----------- self.X : numpy array Frequency spectrum of given frame. """ if self.H is None: warnings.warn("No filter given to the STFT object.") else: np.multiply(self.X, self.H, self.X) def synthesis(self): """ Transform to time domain and reconstruct output with overlap-and-add. Returns ----------- out: numpy array Reconstructed array of samples of length [self.hop] """ # apply IDFT to current frame self.dft.synthesis(self.X) # reconstruct output if self.D==1: self.out[:] = self.dft.x[0:self.hop] if self.zb > 0: self.out[:self.zb] += self.y_p # update state variables self.y_p[:] = self.dft.x[-self.zb:] else: self.out = self.dft.x[0:self.hop,:] if self.zb > 0: self.out[:self.zb,:] += self.y_p[:,:] # update state variables self.y_p[:,:] = self.dft.x[-self.zb:,:] return self.out def get_prev_samples(self): """ Get reconstructed previous samples. """ return self.y_p
CIRCLES_NUM = int(input("How many circles?")) IMAGE_DIRECTOR = f'D:\\' GIFS_DIR = f'D:\\' GIF_FPS = 50 order = CIRCLES_NUM // 2 for image in os.listdir(IMAGE_DIRECTOR): IMAGE_DIR = os.path.join(IMAGE_DIRECTOR, image) GIF_DIR = GIFS_DIR + "\\" + os.path.splitext(image)[0] + ".gif" imageData = Data(IMAGE_DIR) imageData.center() imageData.dataFigure() expSeries = ExponentSeries(imageData, order) DFT(imageData, expSeries) # imageData.plotData() # imageData.plotDFT() # imageData.plotDifferenceDFT() visual = Visualizer(imageData.getXTableDFT(), imageData.getYTableDFT(), expSeries.getExpCoef(), order, imageData.getGrid(), imageData.getFigLim()) anim = visual.visualize() # visual.plotAnimation() anim.save(GIF_DIR, writer=LoopingPillowWriter(fps=GIF_FPS))
def __main__(): results = None try: results = parseArgs() except BaseException as e: print( "ERROR\tIncorrect input syntax: Please check arguments and try again" ) return mode = results.mode image = results.image # run tests DFT.test() if mode == 1: # read the image im_raw = plt.imread(image).astype(float) # pad the image to desired size old_shape = im_raw.shape new_shape = desiredSize(old_shape[0]), desiredSize(old_shape[1]) im = np.zeros(new_shape) im[:old_shape[0], :old_shape[1]] = im_raw # perform fft 2d fft_im = DFT.fast_two_dimension(im) # display fig, ax = plt.subplots(1, 2) ax[0].imshow(im[:old_shape[0], :old_shape[1]], plt.cm.gray) ax[0].set_title('original') ax[1].imshow(np.abs(fft_im), norm=colors.LogNorm()) ax[1].set_title('fft 2d with lognorm') fig.suptitle('Mode 1') plt.show() elif mode == 2: # define a percentage keep fraction keep_ratio = 0.08 # read the image im_raw = plt.imread(image).astype(float) # pad the image to desired size old_shape = im_raw.shape new_shape = desiredSize(old_shape[0]), desiredSize(old_shape[1]) im = np.zeros(new_shape) im[:old_shape[0], :old_shape[1]] = im_raw # perform fft 2d and remove high frequency values fft_im = DFT.fast_two_dimension(im) rows, columns = fft_im.shape print( "Fraction of pixels used {} and the number is ({}, {}) out of ({}, {})" .format(keep_ratio, int(keep_ratio * rows), int(keep_ratio * columns), rows, columns)) fft_im[int(rows * keep_ratio):int(rows * (1 - keep_ratio))] = 0 fft_im[:, int(columns * keep_ratio):int(columns * (1 - keep_ratio))] = 0 # perform ifft 2d to denoise the image denoised = DFT.fast_two_dimension_inverse(fft_im).real # display fig, ax = plt.subplots(1, 2) ax[0].imshow(im[:old_shape[0], :old_shape[1]], plt.cm.gray) ax[0].set_title('original') ax[1].imshow(denoised[:old_shape[0], :old_shape[1]], plt.cm.gray) ax[1].set_title('denoised') fig.suptitle('Mode 2') plt.show() elif mode == 3: # read the image im_raw = plt.imread(image).astype(float) # pad the image to desired size old_shape = im_raw.shape new_shape = desiredSize(old_shape[0]), desiredSize(old_shape[1]) im = np.zeros(new_shape) im[:old_shape[0], :old_shape[1]] = im_raw originalCount = old_shape[0] * old_shape[1] # define compression levels compression = [0, 14, 30, 50, 70, 95] # write down abs of fft im_fft = DFT.fast_two_dimension(im) # render fig, ax = plt.subplots(2, 3) for i in range(2): for j in range(3): compression_level = compression[i * 3 + j] im_compressed = compress_image(im_fft, compression_level, originalCount) ax[i, j].imshow( np.real(im_compressed)[:old_shape[0], :old_shape[1]], plt.cm.gray) ax[i, j].set_title('{}% compression'.format(compression_level)) fig.suptitle('Mode 3') plt.show() elif mode == 4: # define sample runs runs = 10 # run plots fig, ax = plt.subplots() ax.set_xlabel('problem size') ax.set_ylabel('runtime in seconds') ax.set_title('Line plot with error bars') for algo_index, algo in enumerate( [DFT.slow_two_dimension, DFT.fast_two_dimension]): print("starting measurement for {}".format(algo.__name__)) x = [] y = [] problem_size = 2**4 while problem_size <= 2**12: print("doing problem size of {}".format(problem_size)) a = np.random.rand(int(math.sqrt(problem_size)), int(math.sqrt(problem_size))) x.append(problem_size) stats_data = [] for i in range(runs): print("run {} ...".format(i + 1)) start_time = time.time() algo(a) delta = time.time() - start_time stats_data.append(delta) mean = statistics.mean(stats_data) sd = statistics.stdev(stats_data) print("for problem size of {} over {} runs: mean {}, stdev {}". format(problem_size, runs, mean, sd)) y.append(mean) # ensure square and power of 2 problems sizes problem_size *= 4 color = 'r--' if algo_index == 0 else 'g' plt.errorbar(x, y, yerr=sd, fmt=color) plt.show() else: print("ERROR\tMode {} is not recofgnized".format(mode)) return