def get_filted(img, k, sigma): filted = difference_of_gaussians(img, sigma, k * sigma) filter_img = filted * window('hann', img.shape) result = fftshift(np.abs(fftn(filter_img))) result = cv2.normalize(result, result, 0, 1, cv2.NORM_MINMAX) result = np.uint8(result * 255.) return result
def test_window_anisotropic_amplitude(shape): w = window(('tukey', 0.8), shape) # The shape is stretched to give approximately the same range on each axis, # so the center profile should have a similar mean value. profile_w = w[w.shape[0] // 2, :] profile_h = w[:, w.shape[1] // 2] assert abs(profile_w.mean() - profile_h.mean()) < .01
def dct2(array): """ Calculate 2D FFT for array. """ array = array * window('hann', array.shape) f = np.fft.fft2(array) fshift = np.fft.fftshift(f) return fshift
def smooth_gains(obsid, gains, errs): """ Smooth the calibration functions over all time to get the best signal-to-noise """ minobsid = np.min(obsid) maxobsid = np.max(obsid) count = int(maxobsid) - int(minobsid) + 1 allobs = np.zeros(count) allerrs = np.zeros(count) allgain = np.zeros(count) idx = (obsid - minobsid).astype(int) allobs[idx] = obsid allgain[idx] = gains allerrs[idx] = errs idx_sort = np.argsort(allobs) #allobs = np.arange(minobsid,maxobsid+1) allgain = allgain[idx_sort] allobs = allobs[idx_sort] allerrs = allerrs[idx_sort] med = np.nanmedian(gains) bd = (allgain < 0.5) | (allgain > 1) | (np.abs(allgain - med) > 2) | (allerrs > 1e-3) allgain[bd] = np.nan gd = np.isfinite(allgain) #pyplot.errorbar(allobs[gd],allgain[gd],fmt='.',yerr=allerrs[gd],capsize=3) try: allgain[~gd] = np.interp(allobs[~gd], allobs[gd], allgain[gd]) allgain_mdl = filters.median(allgain, filters.window('boxcar', 51)) except ValueError: return allobs, allgain rms = stats.MAD(allgain[gd] - allgain_mdl[gd]) bd = (allgain < 0.5) | (allgain > 1) | (np.abs(allgain - med) > 2) | ( allerrs > 1e-3) | (np.abs(allgain - allgain_mdl) > 3 * rms) allgain[bd] = np.nan gd = np.isfinite(allgain) try: allgain[~gd] = np.interp(allobs[~gd], allobs[gd], allgain[gd]) allgain = filters.median(allgain, filters.window('boxcar', 51)) except ValueError: pass return allobs, allgain
def _find_angle(img_original, img_deformed): # First, band-pass filter both images image = img_original rts_image = img_deformed image = difference_of_gaussians(image, 5, 15) rts_image = difference_of_gaussians(rts_image, 5, 15) # window images wimage = image * window('hann', image.shape) rts_wimage = rts_image * window('hann', rts_image.shape) # work with shifted FFT magnitudes image_fs = np.abs(fftshift(fft2(wimage))) rts_fs = np.abs(fftshift(fft2(rts_wimage))) # Create log-polar transformed FFT mag images and register shape_or = image_fs.shape shape_def = rts_fs.shape radius_or = shape_or[0] // 8 radius_def = shape_def[0] // 8 # only take lower frequencies warped_image_fs = warp_polar(image_fs, radius=radius_or, output_shape=shape_or, scaling='log', order=0) warped_rts_fs = warp_polar(rts_fs, radius=radius_def, output_shape=shape_or, scaling='log', order=0) warped_image_fs = warped_image_fs[:shape_or[ 0], :] # only use half of FFT warped_rts_fs = warped_rts_fs[:shape_or[0], :] shifts, error, phasediff = phase_cross_correlation(warped_image_fs, warped_rts_fs, upsample_factor=10) # Use translation parameters to calculate rotation and scaling parameters shiftr, shiftc = shifts[:2] recovered_angle = (360 / shape_or[0]) * shiftr return recovered_angle
def compute_dgfw( image: np.ndarray, gaussdiff: Sequence[float] = (5, 20), windowweight: float = 1, windowtype: str = "hann", ) -> np.ndarray: """Image Difference of Gaussian Filter + Window. Parameters ---------- image : numpy.ndarray The input image to filter gaussdiff : (float, float), optional The low and high standard deviations for the gaussian difference band pass filter windowweight : float, optional weighting factor scaling beween windowed image and image windowtype : str, optional see `skimage.filters.window` for possible choices Returns ------- numpy.ndarray modified image with bandpass filter and window applied Notes ----- Applying this bandpass and window filter prevents artifacts from image boundaries and noise from contributing significantly to the fourier transform. The gaussian difference filter can be tuned such that the features relevant for the identification of the rotation angle are at the center of the band pass filter. """ image_dgfw = ( difference_of_gaussians(image, *gaussdiff) if gaussdiff[0] < gaussdiff[1] else image * 1.0 ) image_dgfw *= windowweight * window(windowtype, image.shape) + ( 1 - windowweight ) return cast(np.ndarray, image_dgfw)
def test_window_shape_anisotropic(shape): w = window('hann', shape) assert w.shape == shape
###################################################################### # Register rotation and scaling on a translated image - Part 2 # ================================================================= # # We next show how rotation and scaling differences, but not translation # differences, are apparent in the frequency magnitude spectra of the images. # These differences can be recovered by treating the magnitude spectra as # images themselves, and applying the same log-polar + phase correlation # approach taken above. # First, band-pass filter both images image = difference_of_gaussians(image, 5, 20) rts_image = difference_of_gaussians(rts_image, 5, 20) # window images wimage = image * window('hann', image.shape) rts_wimage = rts_image * window('hann', image.shape) # work with shifted FFT magnitudes image_fs = np.abs(fftshift(fft2(wimage))) rts_fs = np.abs(fftshift(fft2(rts_wimage))) # Create log-polar transformed FFT mag images and register shape = image_fs.shape radius = shape[0] // 8 # only take lower frequencies warped_image_fs = warp_polar(image_fs, radius=radius, output_shape=shape, scaling='log', order=0) warped_rts_fs = warp_polar(rts_fs,
def run(img, **args): image = img_as_float(img) wimage = image * window(shape=image.shape, **args) return to_base64(wimage)
def test_window_invalid_shape(): with pytest.raises(ValueError): window(10, shape=(-5, 10)) with pytest.raises(ValueError): window(10, shape=(1.3, 2.0))
def obd(x, y, sf, maxiter, clipping=np.inf, srf=1): # x is estimate of deblurred image # y is observed image # sf size of psf # max iters for gradient descent # srf is super-resolution factor # make sure srf is >= 1 if (srf < 1): raise Exception('Super-resolution factor must be >= 1.') # we will stay consistent with the notation to keep f as the psf sx = np.array(np.shape(x)) sy = np.array(np.shape(y)) # starting flux in y sumy = np.sum(y) ### Update/Guess the PSF #if there is already a guess for x, use it to guess f if sx[0] != 0: # starting flux in x sumx = np.sum(x) # initialize PSF as flat w/ correct intensity f = np.linalg.norm(np.ndarray.flatten(y)) / np.linalg.norm( np.ndarray.flatten(x)) f = f * np.ones(sf) / np.sqrt(np.prod(sf, axis=0)) # srf by rescaling (done once at the front) y = rescale(y, srf, order=1) # mask out sat pixels in y mask = y >= clipping y[mask] = 0 #lets do GD on f given x and y #obd update(f,x,y) for i in range(0, maxiter[0]): #I am everywhere here making assumptions about sx,sf,and sy. #Just let me do that a minute please. ytmp = np.multiply(np.fft.fft2(x, s=sx), np.fft.fft2(f, s=sx)) ytmp = setZero( np.real(np.fft.ifft2(ytmp)) )[sf[0] - 1:, sf[1] - 1:] #so they do not seem to do the np.real here... what does pos mean in that case? ytmp[mask] = 0 Y = np.zeros(sx) Y[sf[0] - 1:, sf[1] - 1:] = y num = np.multiply(np.conj(np.fft.fft2(x, s=sx)), np.fft.fft2(Y, s=sx)) num = setZero(np.real(np.fft.ifft2(num)))[:sf[0], :sf[0]] Y = np.zeros(sx) Y[sf[0] - 1:, sf[1] - 1:] = ytmp denom = np.multiply(np.conj(np.fft.fft2(x, s=sx)), np.fft.fft2(Y, s=sx)) denom = setZero(np.real(np.fft.ifft2(denom)))[:sf[0], :sf[0]] tol = 1e-10 factor = np.divide((num + tol), (denom + tol)) factor = factor * filters.window( ('tukey', 0.1), (sf[0], sf[1]), warp_kwargs={'order': 3}) #attempt to eliminate edge spikes f = np.multiply(f, factor) #this normalization seem suspect for making the light curve sumf = np.sum(f) f = f / sumf # normalize f x = sumf * x # adjust x as well #so this is shifting all the power from f to x #f is always unit normalized #now we guess the structure of x given y and that we have #renormalized x to have the same power as y #now that we have good guess for f, use it to guess x given y #lets do GD on x given f and y #obd update(x,f,y) for i in range(0, maxiter[1]): #I am everywhere here making assumptions about sx,sf,and sy. #Just let me do that a minute please. ytmp = np.multiply(np.fft.fft2(x, s=sx), np.fft.fft2(f, s=sx)) ytmp = setZero( np.real(np.fft.ifft2(ytmp)) )[sf[0] - 1:, sf[1] - 1:] #so they do not seem to do the np.real here... what does pos mean in that case? ytmp[mask] = 0 Y = np.zeros(sx) Y[sf[0] - 1:, sf[1] - 1:] = y num = np.multiply(np.conj(np.fft.fft2(f, s=sx)), np.fft.fft2(Y, s=sx)) num = setZero(np.real(np.fft.ifft2(num))) Y = np.zeros(sx) Y[sf[0] - 1:, sf[1] - 1:] = ytmp denom = np.multiply(np.conj(np.fft.fft2(f, s=sx)), np.fft.fft2(Y, s=sx)) denom = setZero(np.real(np.fft.ifft2(denom))) tol = 1e-10 factor = np.divide((num + tol), (denom + tol)) factor = factor * filters.window( ('tukey', 2 * sf[0] / sx[0]), (sx[0], sx[1]), warp_kwargs={'order': 3}) #attempt to eliminate edge spikes x = np.multiply(x, factor) sumx = np.sum(x) # avg = np.mean((sumx,sumy)) # x = avg/sumx*x x = sumy / sumx * x return x, f, y #intialization of f from scratch else: f = np.zeros(sf) mid = int(f.shape[0] / 2) f[mid, mid] = 1 #delta function intialization # make the guess for x to be size of sy padded by sf #using our intialization of f, use it to guess x given y ## here I am assuming sf<sy sx = sf + sy - 1 if (srf > 1): sx = sf + (srf * sy) - 1 Y = np.zeros(sx) Y[sf[0] - 1:, sf[1] - 1:] = rescale( y, srf, order=1) #should we do something better than bilinear? x = np.multiply(np.conj(np.fft.fft2(f, s=sx)), np.fft.fft2(Y, s=sx)) x = setZero(np.real(np.fft.ifft2(x))) ## to be clear, this is a waste of time, beacuse we know we are choosing x=y1 ## that is what the delta function means. ## This was useful coding practice because it means the image is centered using my conventions ## need to understand why this padding is really necessary ## these lines may be useful for srf cases which we are ignoring rn sumx = np.sum(x) x = sumy / sumx * x return x, f, y
def frequency_filters(img,filter): rows, cols = img.shape crow, ccol = int(rows / 2), int(cols / 2) # center fft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT) fft_shift = np.fft.fftshift(fft) if filter == 'lp': # Circular LPF mask, center circle is 1, remaining all zeros mask = np.zeros((rows, cols, 2), np.uint8) r = 80 center = [crow, ccol] x, y = np.ogrid[:rows, :cols] mask_area = (x - center[0]) ** 2 + (y - center[1]) ** 2 <= r*r mask[mask_area] = 1 # apply mask and inverse FFT fshift = fft_shift * mask fshift_mask_mag = 2000 * np.log(cv2.magnitude(fshift[:, :, 0], fshift[:, :, 1])) f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1]) elif filter == 'hp': # Circular HPF mask, center circle is 0, remaining all ones mask = np.ones((rows, cols, 2), np.uint8) r = 150 center = [crow, ccol] x, y = np.ogrid[:rows, :cols] mask_area = (x - center[0]) ** 2 + (y - center[1]) ** 2 <= r*r # apply mask and inverse FFT fshift = fft_shift * mask fshift_mask_mag = 50 * np.log(cv2.magnitude(fshift[:, :, 0], fshift[:, :, 1])) f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1]) elif filter == 'bp': # Concentric BPF mask,with are between the two cerciles as one's, rest all zero's. mask = np.zeros((rows, cols, 2), np.uint8) r_out = 80 r_in = 5 center = [crow, ccol] x, y = np.ogrid[:rows, :cols] mask_area = np.logical_and(((x - center[0]) ** 2 + (y - center[1]) ** 2 >= r_in ** 2), ((x - center[0]) ** 2 + (y - center[1]) ** 2 <= r_out ** 2)) mask[mask_area] = 1 # apply mask and inverse FFT fshift = fft_shift * mask fshift_mask_mag = 2000 * np.log(cv2.magnitude(fshift[:, :, 0], fshift[:, :, 1])) f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1]) elif filter == 'bp-1': wimage = img * window('hann', img.shape) # window image to improve FFT filtered_image = difference_of_gaussians(img, 1, 1) filtered_wimage = filtered_image * window('hann', img.shape) im_f_mag = fftshift(np.abs(fftn(wimage))) fim_f_mag = fftshift(np.abs(fftn(filtered_wimage))) img = filtered_image elif filter == 'bp-2': wimage = img * window('hann', img.shape) # window image to improve FFT filtered_image = difference_of_gaussians(img, 10) filtered_wimage = filtered_image * window('hann', img.shape) im_f_mag = fftshift(np.abs(fftn(wimage))) fim_f_mag = fftshift(np.abs(fftn(filtered_wimage))) img = filtered_image return cv2.normalize(img,None,0,255,cv2.NORM_MINMAX,dtype=cv2.CV_8U )
def obd(x,y,sf,maxiter): # x is estimate of deblurred image # y is observed image # sf size of psf # max iters for gradient descent # we will stay consistent with the notation to keep f as the psf sx = np.array(np.shape(x)) sy = np.array(np.shape(y)) ### Update/Guess the PSF #if there is already a guess for x, use it to guess f if sx[0] != 0: # initialize PSF as flat w/ correct intensity f = np.linalg.norm(np.ndarray.flatten(y)) / np.linalg.norm(np.ndarray.flatten(x)) f = f * np.ones(sf) / np.sqrt(np.prod(sf, axis=0)) #lets do GD on f given x and y #obd update(f,x,y) for i in range(0,maxiter[0]): #I am everywhere here making assumptions about sx,sf,and sy. #Just let me do that a minute please. ytmp = np.multiply(np.fft.fft2(x,s=sx), np.fft.fft2(f, s=sx)) ytmp = setZero(np.real(np.fft.ifft2(ytmp)))[sf[0]-1:,sf[1]-1:] #so they do not seem to do the np.real here... what does pos mean in that case? Y = np.zeros(sx) Y[sf[0]-1:,sf[1]-1:] = y num = np.multiply(np.conj(np.fft.fft2(x,s=sx)),np.fft.fft2(Y,s=sx)) num = setZero(np.real(np.fft.ifft2(num)))[:sf[0],:sf[0]] Y = np.zeros(sx) #,dtype=np.complex64) Y[sf[0]-1:,sf[1]-1:] = ytmp denom = np.multiply(np.conj(np.fft.fft2(x,s=sx)),np.fft.fft2(Y,s=sx)) denom = setZero(np.real(np.fft.ifft2(denom)))[:sf[0],:sf[0]] tol = 1e-10 factor = np.divide((num+tol),(denom+tol)) factor = factor*filters.window(('tukey',0.3),(sf[0],sf[1]),warp_kwargs={'order':3}) #attempt to eliminate edge spikes f = np.multiply(f, factor) #this normalization seem suspect for making the light curve sumf = np.sum(f) f = f/sumf # normalize f x = sumf*x # adjust x as well #so this is shifting all the power from f to x #f is always unit normalized #now we guess the structure of x given y and that we have #renormalized x to have the same power as y ## actually we are normalizing by abs(image) not image ## power. This makes me feel uncomfortable. #now that we have good guess for f, use it to guess x given y #lets do GD on x given f and y #obd update(x,f,y) for i in range(0,maxiter[1]): #I am everywhere here making assumptions about sx,sf,and sy. #Just let me do that a minute please. ytmp = np.multiply(np.fft.fft2(x,s=sx), np.fft.fft2(f, s=sx)) ytmp = setZero(np.real(np.fft.ifft2(ytmp)))[sf[0]-1:,sf[1]-1:] #so they do not seem to do the np.real here... what does pos mean in that case? Y = np.zeros(sx) Y[sf[0]-1:,sf[1]-1:] = y num = np.multiply(np.conj(np.fft.fft2(f,s=sx)),np.fft.fft2(Y,s=sx)) num = setZero(np.real(np.fft.ifft2(num))) Y = np.zeros(sx) #,dtype=np.complex64) Y[sf[0]-1:,sf[1]-1:] = ytmp denom = np.multiply(np.conj(np.fft.fft2(f,s=sx)),np.fft.fft2(Y,s=sx)) denom = setZero(np.real(np.fft.ifft2(denom))) tol = 1e-10 factor = np.divide((num+tol),(denom+tol)) factor = factor*filters.window(('tukey',0.3),(sx[0],sx[1]),warp_kwargs={'order':3}) #attempt to eliminate edge spikes x = np.multiply(x, factor) return x, f #intialization of f from scratch else: f = np.zeros(sf) mid = int(f.shape[0]/2) f[mid,mid] = 1 #delta function intialization # make the guess for x to be size of sy padded by sf #using our intialization of f, use it to guess x given y ## here I am assuming sf<sy sx = sf + sy - 1 Y = np.zeros(sx) Y[sf[0]-1:,sf[1]-1:] = y x = np.multiply(np.conj(np.fft.fft2(f,s=sx)),np.fft.fft2(Y,s=sx)) x = setZero(np.real(np.fft.ifft2(x))) ## to be clear, this is a waste of time, beacuse we know we are choosing x=y1 ## that is what the delta function means. ## This was useful coding practice because it means the image is centered using my conventions ## need to understand why this padding is really necessary ## these lines may be useful for srf cases which we are ignoring rn return x, f
in the figure). The application of a two-dimensional Hann window greatly reduces the spectral leakage, making the "real" frequency information more visible in the plot of the frequency component of the FFT. """ import matplotlib.pyplot as plt import numpy as np from scipy.fftpack import fft2, fftshift from skimage import img_as_float from skimage.color import rgb2gray from skimage.data import astronaut from skimage.filters import window image = img_as_float(rgb2gray(astronaut())) wimage = image * window('hann', image.shape) image_f = np.abs(fftshift(fft2(image))) wimage_f = np.abs(fftshift(fft2(wimage))) fig, axes = plt.subplots(2, 2, figsize=(8, 8)) ax = axes.ravel() ax[0].set_title("Original image") ax[0].imshow(image, cmap='gray') ax[1].set_title("Windowed image") ax[1].imshow(wimage, cmap='gray') ax[2].set_title("Original FFT (frequency)") ax[2].imshow(np.log(image_f), cmap='magma') ax[3].set_title("Window + FFT (frequency)") ax[3].imshow(np.log(wimage_f), cmap='magma') plt.show()
two applications of the Difference of Gaussians approach for band-pass filtering. """ ###################################################################### # Denoise image and reduce shadows # ================================ import matplotlib.pyplot as plt import numpy as np from skimage.data import gravel from skimage.filters import difference_of_gaussians, window from scipy.fftpack import fftn, fftshift image = gravel() wimage = image * window('hann', image.shape) # window image to improve FFT filtered_image = difference_of_gaussians(image, 1, 12) filtered_wimage = filtered_image * window('hann', image.shape) im_f_mag = fftshift(np.abs(fftn(wimage))) fim_f_mag = fftshift(np.abs(fftn(filtered_wimage))) fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(8, 8)) ax[0, 0].imshow(image, cmap='gray') ax[0, 0].set_title('Original Image') ax[0, 1].imshow(np.log(im_f_mag), cmap='magma') ax[0, 1].set_title('Original FFT Magnitude (log)') ax[1, 0].imshow(filtered_image, cmap='gray') ax[1, 0].set_title('Filtered Image') ax[1, 1].imshow(np.log(fim_f_mag), cmap='magma') ax[1, 1].set_title('Filtered FFT Magnitude (log)') plt.show()
def test_window_type(wintype): w = window(wintype, (9, 9)) assert w.ndim == 2 assert w.shape[1:] == w.shape[:-1] assert np.allclose(w.sum(axis=0), w.sum(axis=1))
def test_window_1d(size): w = window('hann', size) w1 = get_window('hann', size, fftbins=False) assert np.allclose(w, w1)
def SOBEL_FFT(channel): image = filters.sobel(channel) image = filters.difference_of_gaussians(image, 0, 12) image = image * filters.window('hann', (32, 32)) image = fftshift(np.abs(fftn(image))) return np.log(image)
def test_window_shape_isotropic(size, ndim): w = window('hann', (size, ) * ndim) assert w.ndim == ndim assert w.shape[1:] == w.shape[:-1] for i in range(1, ndim - 1): assert np.allclose(w.sum(axis=0), w.sum(axis=i))
def FFT_SOBEL(channel): filtered_image = filters.difference_of_gaussians(channel, 1, 12) wimage = filtered_image * filters.window('hann', channel.shape) im_f_mag = fftshift(np.abs(fftn(wimage))) image = filters.sobel(np.log(im_f_mag)) return image