def PSNR(f, g, max_val=None): """ ~ Calculates peak signal-to-noise ratio of an array and its noisy approximation ========input========= f : Input noise-free array g : Input noisy approximation array max_val : Maximum pixel value ========output======== psnr : PSNR """ f = tl.array(f) g = tl.array(g) if (tl.shape(f) != tl.shape(g)): raise TypeError('f and g must be arrays of equal shape') if (max_val == None): max_val = tl.maxx([f, g]) if ((f == g).all()): psnr = tl.inf else: mse = tl.mean(tl.abss(f - g)**2) psnr = 10 * tl.log10(max_val**2 / mse) return psnr
def profile(self, z, f, dx=1.): """ Beam's complex amplitude at an axial distance z from waist. Plane lies perpendicular to z axis and has the same shape as f. ========Input========= z : Axial distance from waist f : 2D Array defining the output array shape dx : Pixel pitch (default value is unit of measurement) ========Raises======== TypeError: If f isn't a 2D array ========Output======== g : 2D complex array (complex amplitude at an axial distance z from waist centered at z axis) """ if (len(tl.shape(f)) != 2): raise TypeError('f must be a 2D array') n, m = tl.shape(f) x = tl.arange(-m * dx / 2, m * dx / 2, dx) y = tl.arange(-n * dx / 2, n * dx / 2, dx) X, Y = tl.meshgrid(x, y) R = tl.sqrt(X**2 + Y**2) g = self.value(R, z) return g
def profile(self, z, f, dx=1.): """ Beam's complex amplitude at an axial distance z from waist. Plane lies perpendicular to z axis and has the same shape as f. ========Input========= z : Axial distance from waist f : 2D Array defining the output array shape dx : Pixel pitch (default value is unit of measurement) ========Raises======== ========Output======== g : 2D complex array (complex amplitude at an axial distance z from waist centered at z axis) """ n, m = tl.shape(f) x = tl.arange(-m * dx / 2, m * dx / 2, dx) y = tl.arange(-n * dx / 2, n * dx / 2, dx) X, Y = tl.meshgrid(x, y) g = self.value(X, Y, z) return g
def GetRBW(f, dx=1., dy=None, p=0.98): """ ~ Approximately calculates full radial bandwidth ~ This is not a verified numerical method just something I made up ========Input========= f : Input 2D array p : Effectiveness parameter dx : Horizontal sampling interval (Default value is unit of measurement) dy : Vertical sampling interval (Default for square sampling) ========output======== RBW : Full radial bandwidth. """ if (dy == None): dy = dx n, m = tl.shape(f) M = max([n, m]) F = FFT2(f) P = Power(F) # Total power fr = tl.arange(1, M + 1) # Radial frequency in pixels Pr = [Power(F * Ellipse(fri, fri, (n, m), use_pxc=True)) for fri in fr ] # Power as function of radial distance in the Fourier domain RBW = 2 * max(fr[Pr <= p * P]) * max([1. / dx, 1. / dy]) return RBW
def HighPassF(f, px=0.5, py=0.5, mask='rect'): """ ~ High pass filter ========Input========= f : Input 2D array px : Characteristic horizontal length of filter function py : Characteristic vertical length of filter function mask : Filter function ========output======== g : Filtered signal """ shape = tl.shape(f) filters = { 'rect': Rect(px, py, shape), 'ellipse': Ellipse(px, py, shape), 'gauss': Gauss(px, py, shape) } F = FFT2(f) F *= (1. - filters.get(mask)) g = IFFT2(F) return g
def Entropy(f, nbits=0): """ ~ Calculates entropy of array histogram ========input========= f : Input array nbits : Calculate for a given number of bits ========output======== S : Shannon Entropy """ f = tl.reshape(f, (1, tl.prod(tl.shape(f))))[0] if (nbits != 0): f = LinearNormal(f) MAX = 2.**nbits - 1 f = tl.floor(f * MAX) pdf = tl.Series(f).value_counts().values pdf = pdf / tl.summ(pdf * 1.) S = -tl.summ(pdf * tl.log2(pdf)) return S
def LinearNormal(f, new_min=0., new_max=1.): """ ~ Linearly normalizes f to interval [new_min,new_max] ~ If the input array is complex, only affects its norm ========Input========= f : Input array new_min : Minimum value in normalization interval (default 0) new_max : Maximum value in normalization interval (default 1) ========Output======== g : Normalized input array """ if tl.iscomplex(f).any(): fnorm = tl.abss(f) if ((fnorm == tl.mean(fnorm)).all()): g = 1. else: g = (fnorm - tl.minn(fnorm)) / (tl.maxx(fnorm) - tl.minn(fnorm) ) * (new_max - new_min) + new_min g *= tl.exp(1j * tl.angle(f)) else: if ((f == tl.mean(f)).all()): g = tl.ones(tl.shape(f)) else: g = (f - tl.minn(f)) / (tl.maxx(f) - tl.minn(f)) * (new_max - new_min) + new_min return g
def FresnelIR(f, z, wl, dx): """ ~ Propagates f in free space sampling the Impulse Response Voelz D. Computational Fourier Optics: A MATLAB Tutorial. 2011. ========Input========= f : Input complex amplitude profile (2D complex array) z : Propagation distance wl : Central wavelenght of the field dx : Sampling interval (default value is unit of measurement) ========Output======== h : Propagated complex amplitude profile (2D complex array) """ n, m = tl.shape(f) Lx = dx * n Ly = dx * m F = FFT2(f) x = tl.arange(-Lx / 2, Lx / 2, dx) y = tl.arange(-Ly / 2, Ly / 2, dx) X, Y = tl.meshgrid(x, y) g = tl.exp(1j * tl.pi / (wl * z) * (X**2 + Y**2)) / (1j * wl * z) G = FFT2(g) * dx * dx h = IFFT2(F * G) return h
def FresnelTF(f, z, wl, dx): """ ~ Propagates f in free space sampling the Transfer Function Voelz D. Computational Fourier Optics: A MATLAB Tutorial. 2011. ========Input========= f : Input complex amplitude profile (2D complex array) z : Propagation distance wl : Central wavelenght of the field dx : Sampling interval (default value is unit of measurement) SBC : Use the source bandwidth criterion for large propagation regime ========Output======== h : Propagated complex amplitude profile (2D complex array) """ n, m = tl.shape(f) Lx = dx * n Ly = dx * m F = FFT2(f) fx = tl.arange(-1 / (2 * dx), 1 / (2 * dx), 1 / Lx) fy = tl.arange(-1 / (2 * dx), 1 / (2 * dx), 1 / Ly) FX, FY = tl.meshgrid(fx, fy) G = tl.exp((-1j * wl * tl.pi * z) * (FX**2 + FY**2)) h = IFFT2(F * G) return h
def Fraunhofer(f, z, wl, dx=1.): """ ~ Propagates input 2D array in free space using the Fraunhofer propagator Voelz D. Computational Fourier Optics: A MATLAB Tutorial. 2011. ========Input========= f : Input complex amplitude profile (2D complex array) z : Propagation distance wl : Central wavelenght of the field dx : Sampling interval (default value is unit of measurement) ========Output======== g : Propagated complex amplitude profile (2D complex array) oL : Observation plane side lenght """ n, m = tl.shape(f) oL = wl * z / dx # Observation plane side lenght odx = oL / m ody = oL / n # Observation plane sample interval x = tl.arange(-oL / 2, oL / 2, odx) y = tl.arange(-oL / 2, oL / 2, ody) X, Y = tl.meshgrid(x, y) g = -1j / (wl * z) * tl.exp(1j * tl.pi / (wl * z) * (X**2 + Y**2)) g = g * FFT2(f) * dx * dx return g, oL
def Embed(f, g, x=0., y=0.): """ ~ Embeds g onto f centered at (x,y) ~ (x,y) coordinates are given in a centered cartesian frame ========Input========= f : 2D input array g : 2D array to be embedded x : x cartesian coordinate of the embedding center measured from input array's center y : y cartesian coordinate of the embedding center measured from input array's center ========Raises======== ValueError : If either f or g isn't a 2D array ValueError : If g is bigger than f ========Output======== h : 2D input array f with g embedded at (x,y) """ if ((tl.ndim(f), tl.ndim(g)) != (2, 2)): raise ValueError('Input and embedding arrays must be 2D') (m, n) = tl.shape(f) (mm, nn) = tl.shape(g) if (mm > m or nn > n): raise ValueError('Embedding array must be smaller than input array') h = f.copy() h[tl.int32(tl.floor((m - mm) * 0.5) + y):tl.int32(tl.floor((m + mm) * 0.5) + y), tl.int32(tl.floor((n - nn) * 0.5) + x):tl.int32(tl.floor((n + nn) * 0.5) + x)] = g return h
def Fresnel2S(f, z, wl, L1, L2): """ ~ Propagates input 2D array in free space using the two-step Fresnel propagator Voelz D. Computational Fourier Optics: A MATLAB Tutorial. 2011. ========Input========= f : Input complex amplitude profile (2D complex array) z : Propagation distance wl : Central wavelenght of the field L1 : Input sample plane side lenght L2 : Output sample plane side lenght ========Output======== h : Propagated complex amplitude profile (2D complex array) """ if (tl.ndim(f) != 2): raise TypeError("Input array must be a 2D square array") m, n = tl.shape(f) if (m != n): raise ValueError("Input array must be a 2D square array") k = 2 * tl.pi / wl # Source plane dx1 = L1 / m x1 = tl.arange(-L1 / 2., L1 / 2., dx1) X, Y = tl.meshgrid(x1, x1) F = f * tl.exp(1j * k / (2. * z * L1) * (L1 - L2) * (X**2 + Y**2)) F = tl.fft.fft2(tl.fft.fftshift(F)) # Dummy plane fx1 = tl.arange(-1. / (2 * dx1), 1. / (2 * dx1), 1. / L1) fx1 = tl.fft.fftshift(fx1) FX1, FY1 = tl.meshgrid(fx1, fx1) G = F * tl.exp(-1j * tl.pi * wl * z * L1 / L2 * (FX1**2 + FY1**2)) g = tl.fft.ifftshift(tl.fft.ifft2(G)) # Observation plane dx2 = L2 / m x2 = tl.arange(-L2 / 2., L2 / 2., dx2) X, Y = tl.meshgrid(x2, x2) g = (L2 / L1) * g * tl.exp(-1j * k / (2. * z * L2) * (L1 - L2) * (X**2 + Y**2)) g = g * (dx1 / dx2)**2 return g
def ErrDiffBin(f, T=0.5, b=[[1 / 16., 5 / 16., 3 / 16.], [7 / 16., 0., 0.], [0., 0., 0.]]): """ ~ Binarizes input 2D array using the Error-Diffusion algorithm. Barnard E. Optimal error diffusion for computer-generated holograms. J Opt Soc Am A 1988;5:1803-17. ========Input========= f : Real input 2D array T : Binarization threshold (default value for input array with [0,1] dynamic range) b : 3x3 2D real array with the diffusion coefficients (default given by paper; see paper for details) ========Raises======== TypeError : If b isn't a 3x3 2D real array ValueError : If b doesn't meet the convergence conditions (see paper for details) ========Output======== h[1:-1,1:-1] : Binarized input array """ b = tl.array(b) if (tl.shape(b) != (3, 3) or tl.isreal(b).all() == False): raise TypeError('b has to be a 3x3 2D array') if (tl.summ(abs(b)) > 1): raise ValueError( 'The diffusion coefficients don\'t meet the convergence conditions' ) n, m = f.shape g = tl.zeros((n + 2, m + 2)) h = tl.zeros((n + 2, m + 2)) s = tl.zeros((n + 2, m + 2)) g[1:-1, 1:-1] = f for i in range(1, n - 1): for j in range(1, m - 1): g[i, j] += tl.summ(b * s[i - 1:i + 2, j - 1:j + 2]) h[i, j] = (g[i, j] > T) * 1. s[i, j] = g[i, j] - h[i, j] return h[1:-1, 1:-1]
def FresnelCS(f, z, wl, dx=1., SBC=False): """ ~ Propagates input 2D array in free space using the critical sampling criterion ~ Samples either the Impulse Response or the Transfer Function given the critic sampling criterion Voelz D. Computational Fourier Optics: A MATLAB Tutorial. 2011. ========Input========= f : Input complex amplitude profile (2D complex array) z : Propagation distance wl : Central wavelenght of the field dx : Sampling interval (default value is unit of measurement) SBC : Use the source bandwidth criterion for large propagation regime ========Output======== h : Propagated complex amplitude profile (2D complex array) """ n, m = tl.shape(f) # Array dimensions Lx = dx * m Ly = dx * n # Source plane side lenghts zc = dx * Lx / wl # Critic sampling propagation distance F = FFT2(f) if (SBC): # Check the source bandwidth criterion B = GetRBW(f, dx) SBC = B <= min([Lx, Ly]) / (wl * z) if (abs(z) > zc and not SBC): # Propagating using Impulse Response x = tl.arange(-Lx / 2., Lx / 2., dx) y = tl.arange(-Ly / 2., Ly / 2., dx) X, Y = tl.meshgrid(x, y) g = tl.exp(1j * tl.pi / (wl * z) * (X**2 + Y**2)) / (1j * wl * z) G = FFT2(g) * dx * dx if (abs(z) <= zc): # Propagating using Transfer Function F = 0.5 / dx # Nyquist frequency fx = tl.arange(-F, F, 1. / Lx) fy = tl.arange(-F, F, 1. / Ly) FX, FY = tl.meshgrid(fx, fy) G = tl.exp((-1j * wl * tl.pi * z) * (FX**2 + FY**2)) h = IFFT2(F * G) return h
def Trim(f, shape, x=0., y=0.): """ ~ Trims an array from f centered at (x,y) with shape shape ~ (x,y) coordinates are given in a centered cartesian frame ========Input========= f : 2D input array from which the trim is taken shape : Trimming shape x : x cartesian coordinate of the trimming center measured from input array's center y : y cartesian coordinate of the trimming center measured from input array's center ========Raises======== TypeError : If input array isn't 2D TypeError : If trimming shape isn't 2D ValueError : If trimming shape is bigger than input array's shape ========Output======== h : Trimmed 2D array """ if (tl.ndim(f) != 2): raise TypeError('Input array must be 2D') if (len(shape) != 2): raise TypeError('Trimming shape must be 2D') (m, n) = tl.shape(f) (mm, nn) = shape if (mm > m or nn > n): raise ValueError('Trimming array must be smaller than input array') h = f[tl.int32(tl.floor((m - mm) * 0.5) + y):tl.int32(tl.floor((m + mm) * 0.5) + y), tl.int32(tl.floor((n - nn) * 0.5) + x):tl.int32(tl.floor((n + nn) * 0.5) + x)] return h