def __init__(self, nx=256, dx=0.1, l=0.68, n=1.33, NA=1.27, f=9000, wavelengths=10, wavelength_halfmax=0.005): dx = float(dx) self.dx = dx l = float(l) n = float(n) NA = float(NA) f = float(f) self.nx = nx self.ny = nx Pupil.__init__(self, l, n, NA, f) self.numWavelengths = wavelengths # Frequency sampling: dk = 1 / (nx * dx) # Pupil function pixel grid: x, y = _np.mgrid[-nx / 2.:nx / 2., -nx / 2.:nx / 2.] + 0.5 self.x_pxl = x self.y_pxl = y self.r_pxl = _msqrt(x**2 + y**2) # Pupil function frequency space: kx = dk * x ky = dk * y self.k = _msqrt(kx**2 + ky**2) # Axial Fourier space coordinate: self.kz = _msqrt((n / l)**2 - self.k**2) self.kzs = _np.zeros( (self.numWavelengths, self.kz.shape[0], self.kz.shape[1]), dtype=self.kz.dtype) ls = _np.linspace(l - wavelength_halfmax, l + wavelength_halfmax, self.kzs.shape[0]) for i in range(0, self.kzs.shape[0]): self.kzs[i] = _msqrt((n / ls[i])**2 - self.k**2) # Scaled pupil function radial coordinate: self.r = self.k / self.k_max self.s = self.unit_disk_to_spatial_radial_coordinate(self.r) self.alpha = self.spatial_radial_coordinate_to_optical_angle(self.s) self.m = self.spatial_radial_coordinate_to_xy_slope(self.alpha) # Plane wave: self.plane = _np.ones((nx, nx)) + 1j * _np.zeros((nx, nx)) self.plane[self.k > self.k_max] = 0 self.pupil_npxl = abs(self.plane.sum()) self.kx = kx self.theta = _np.arctan2(y, x)
def __init__(self, nx=256, dx=0.1, l=0.68, n=1.33, NA=1.27, f=3333.33, wavelengths=10, wave_step=0.005): dx = float(dx) self.dx = dx l = float(l) n = float(n) NA = float(NA) f = float(f) self.nx = nx self.ny = nx Pupil.__init__(self, l, n, NA, f) self.numWavelengths = wavelengths dk = 1 / (nx * dx) self.k_pxl = int(self.k_max / dk) print("The pixel radius of pupil:", self.k_pxl) # Pupil function pixel grid: Mx, My = _np.mgrid[-nx / 2.:nx / 2., -nx / 2.:nx / 2.] + 0.5 self.x_pxl = Mx # pixel grid in x self.y_pxl = My # pixel grid in y self.r_pxl = _msqrt(Mx**2 + My**2) # why the x,y,r_pxl are dimensionless? # Pupil function frequency space: kx = dk * Mx ky = dk * My self.k = _msqrt( kx**2 + ky**2) # This is in the unit of 1/x # this is a 2-D array out_pupil = self.k > self.k_max # Axial Fourier space coordinate: self.kz = _msqrt((n / l)**2 - self.k**2) self.kz[out_pupil] = 0 self.kzs = _np.zeros( (self.numWavelengths, self.kz.shape[0], self.kz.shape[1]), dtype=self.kz.dtype) ls = _np.linspace(l - wave_step, l + wave_step, self.numWavelengths) for i in range(0, self.kzs.shape[0]): self.kzs[i] = _msqrt((n / ls[i])**2 - self.k**2) self.kzs[i, out_pupil] = 0 # Scaled pupil function radial coordinate: self.r = self.k / self.k_max # Should be dimension-less self.s = self.unit_disk_to_spatial_radial_coordinate( self.r) # The real radius of the pupil. # Plane wave: self.plane = _np.ones((nx, nx)) + 1j * _np.zeros((nx, nx)) self.plane[self.k > self.k_max] = 0 # Outside the pupil: set to zero self.pupil_npxl = abs(self.plane.sum()) # how many non-zero pixels self.kx = kx # This is not used self.theta = _np.arctan2(My, Mx) # Polar coordinate: angle
def __init__(self, nx=256, dx=0.1, l=0.68, n=1.33, NA=1.27, f=3333.33, wavelengths=10, wavelength_halfmax=0.005): dx = float(dx) self.dx = dx l = float(l) n = float(n) NA = float(NA) f = float(f) self.nx = nx self.ny = nx Pupil.__init__(self, l, n, NA, f) self.numWavelengths = wavelengths # Frequency sampling: dk = 1/(nx*dx) # What should dx be? self.k_pxl = int(self.k_max/dk) print("The pixel radius of pupil:", self.k_pxl) # Pupil function pixel grid: Mx,My = _np.mgrid[-nx/2.:nx/2.,-nx/2.:nx/2.]+0.5 self.x_pxl = Mx # pixel grid in x self.y_pxl = My # pixel grid in y self.r_pxl = _msqrt(Mx**2+My**2) # why the x,y,r_pxl are dimensionless? # Pupil function frequency space: kx = dk*Mx ky = dk*My self.k = _msqrt(kx**2+ky**2) # This is in the unit of 1/x # this is a 2-D array # self.k = _msqrt(kx**2+ky**2)# This is in the unit of 1/x # this is a 2-D array out_pupil = self.k>self.k_max # Axial Fourier space coordinate: self.kz = _msqrt((n/l)**2-self.k**2) self.kz[out_pupil] = 0 # self.kz.imag = 0 # brutal force print(self.kz.dtype) self.kzs = _np.zeros((self.numWavelengths,self.kz.shape[0],self.kz.shape[1]),dtype=self.kz.dtype) ls = _np.linspace(l-wavelength_halfmax,l+wavelength_halfmax,self.numWavelengths) for i in range(0,self.kzs.shape[0]): self.kzs[i] = _msqrt((n/ls[i])**2-self.k**2) self.kzs[i,out_pupil] = 0 # self.kzs.imag = 0 # brutal force # Scaled pupil function radial coordinate: self.r = self.k/self.k_max # Should be dimension-less self.s = self.unit_disk_to_spatial_radial_coordinate(self.r) # The real radius of the pupil. self.alpha = self.spatial_radial_coordinate_to_optical_angle(self.s) self.m = self.spatial_radial_coordinate_to_xy_slope(self.alpha) # Should this be inverted? And self.m is not used at all... # Plane wave: self.plane = _np.ones((nx,nx))+1j*_np.zeros((nx,nx)) self.plane[self.k>self.k_max] = 0 # Outside the pupil: set to zero self.pupil_npxl = abs(self.plane.sum()) # how many non-zero pixels self.kx = kx # This is not used self.theta = _np.arctan2(My,Mx) # Polar coordinate: angle
def __init__(self, control, im_size, maskRadius, maskCenter, pixelSize, diffLimit, plotWidget, varyZern=False): QtCore.QThread.__init__(self) self.control = control self.pixelSize = pixelSize self.diffLimit = diffLimit self.maskRadius = maskRadius self.maskCenter = maskCenter self.plotWidget = plotWidget self._on = True self._varyZern = varyZern self.numZernsToVary = 100 self._zerns = 0 nx,ny = im_size if maskCenter[0] > -1 and maskCenter[1]>-1: x,y = np.meshgrid(np.arange(nx),np.arange(ny)) x -= maskCenter[0] y -= maskCenter[1] r_pxl = _msqrt(x**2 + y**2) mask = r_pxl<maskRadius self.mask = mask else: self.mask = None
def __init__(self, control, im_size, maskRadius, maskCenter, pixelSize, diffLimit, plotWidget, numToVary=100, varyZern=False, wait_time=0.1): ''' Finds sharpness Parameters :control: class :im_size: (int,int) :maskRadius: int :maskCenter: int :pixelSize: int :diffLimit: int Diffraction limited resolution (in same units as pixelSize) :plotWidget: :numToVary: int Optional :varyZern: boolean Optional :wait_time: float Optional. Time between patterns applied to adaptive optics device (seconds) Emits signal 'nextModulation(int)' when ready to add new modulation... nextModulation takes parameter that counts upward from 0 Emits signal 'doneAdvancingZern' when done ''' QtCore.QThread.__init__(self) self.control = control self.pixelSize = pixelSize self.diffLimit = diffLimit self.maskRadius = maskRadius self.maskCenter = maskCenter self.plotWidget = plotWidget self.wait_time = wait_time self._on = True self._varyZern = varyZern self.numZernsToVary = numToVary self._zerns = 0 nx, ny = im_size if maskCenter[0] > -1 and maskCenter[1] > -1: x, y = np.meshgrid(np.arange(nx), np.arange(ny)) x -= maskCenter[0] y -= maskCenter[1] r_pxl = _msqrt(x**2 + y**2) mask = r_pxl < maskRadius self.mask = mask else: self.mask = None
def retrievePF(self, bscale=1.00, psf_diam=50, resample=None): # an ultrasimplified version # comment on 08/12: I am still not convinced of the way of setting background. z_offset, zz = psf_zplane(self.PSF, self.dz, self.l / 3.2) # This should be the reason!!!! >_< A = self.PF.plane # z_offset = -z_offset # To deliberately add something wrong Mx, My = np.meshgrid( np.arange(self.nx) - self.nx / 2., np.arange(self.nx) - self.nx / 2.) r_pxl = _msqrt(Mx**2 + My**2) bk_inner = 16 bk_outer = 20 hcyl = np.array(self.nz * [np.logical_and(r_pxl >= bk_inner, r_pxl < bk_outer)]) background = np.mean(self.PSF[hcyl]) * bscale print(" background = ", background) print(" z_offset = ", z_offset) if (resample == False): PSF_sample = self.PSF zs = zz - z_offset else: PSF_sample = self.PSF[::resample] zs = zz[::resample] - z_offset complex_PF = self.PF.psf2pf(PSF_sample, zs, background, A, self.nIt) Pupil_final = _PupilFunction(complex_PF, self.PF) self.pf_complex = Pupil_final.complex self.pf_phase = unwrap_phase(Pupil_final.phase) self.pf_ampli = Pupil_final.amplitude
def compute_pupil_function(z): phase = (2*_np.pi*n*(_msqrt(f**2*(1+m**2)-z**2)-m*z))/ \ (l*_msqrt(1+m**2)) if coverslip_tilt != 0: # Angle between ray and coverslip normal: d = _np.arccos(_np.sin(a)*_np.sin(e) * \ (_np.cos(t)*_np.cos(g)+_np.sin(t)*_np.sin(g)) + \ _np.cos(a)*_np.cos(e)) # Path length through coverslip: p = d / _np.sqrt(1 + (n * _np.sin(d) / ng)**2) # The correction collar takes care of none tilt based differences: p -= d / _np.sqrt(1 + (n * _np.sin(a) / ng)**2) # Path length to phase conversion: p *= ng * 2 * _np.pi / l phase += p PF = _np.sqrt(n_photons / self.pupil_npxl) * _np.exp(1j * phase) PF = _np.nan_to_num(PF) return self.apply_NA_restriction(PF)
def __init__(self, control, im_size, maskRadius, maskCenter, pixelSize, diffLimit, plotWidget, numToVary = 100, varyZern=False, wait_time = 0.1): ''' Finds sharpness Parameters :control: class :im_size: (int,int) :maskRadius: int :maskCenter: int :pixelSize: int :diffLimit: int Diffraction limited resolution (in same units as pixelSize) :plotWidget: :numToVary: int Optional :varyZern: boolean Optional :wait_time: float Optional. Time between patterns applied to adaptive optics device (seconds) Emits signal 'nextModulation(int)' when ready to add new modulation... nextModulation takes parameter that counts upward from 0 Emits signal 'doneAdvancingZern' when done ''' QtCore.QThread.__init__(self) self.control = control self.pixelSize = pixelSize self.diffLimit = diffLimit self.maskRadius = maskRadius self.maskCenter = maskCenter self.plotWidget = plotWidget self.wait_time = wait_time self._on = True self._varyZern = varyZern self.numZernsToVary = numToVary self._zerns = 0 nx,ny = im_size if maskCenter[0] > -1 and maskCenter[1]>-1: x,y = np.meshgrid(np.arange(nx),np.arange(ny)) x -= maskCenter[0] y -= maskCenter[1] r_pxl = _msqrt(x**2 + y**2) mask = r_pxl<maskRadius self.mask = mask else: self.mask = None
def __init__(self, size, cx, cy, d): # cx, cy: the pupil center self.cx = float(cx) self.cy = float(cy) self.d = float(d) self.size = size self.nx, self.ny = size self.x_pxl, self.y_pxl = _np.meshgrid(_np.arange(self.nx),_np.arange(self.ny)) self.x_pxl -= cx self.y_pxl -= cy self.r_pxl = _msqrt(self.x_pxl**2+self.y_pxl**2) # the radial coordinate in the unit of 1 (grid number ) self.r = 2.0*self.r_pxl/d # the radial coordinate coordinate in the unit of length. Theoretically between 0 and 1. self.theta = _np.arctan2(self.y_pxl, self.x_pxl) self.x = 2.0*self.x_pxl/d # so self.x and y are in the range of (0,1) self.y = 2.0*self.y_pxl/d # But, isn't d the same with nx, ny?
def compute_pupil_function(z): phase = (2*_np.pi*n*(_msqrt(f**2*(1+m**2)-z**2)-m*z))/ \ (l*_msqrt(1+m**2)) if coverslip_tilt != 0: # Angle between ray and coverslip normal: d = _np.arccos(_np.sin(a)*_np.sin(e) * \ (_np.cos(t)*_np.cos(g)+_np.sin(t)*_np.sin(g)) + \ _np.cos(a)*_np.cos(e)) # Path length through coverslip: p = d/_np.sqrt(1+(n*_np.sin(d)/ng)**2) # The correction collar takes care of none tilt based differences: p -= d/_np.sqrt(1+(n*_np.sin(a)/ng)**2) # Path length to phase conversion: p *= ng*2*_np.pi/l phase += p PF = _np.sqrt(n_photons/self.pupil_npxl)*_np.exp(1j*phase) PF = _np.nan_to_num(PF) return self.apply_NA_restriction(PF) if _np.ndim(z) == 0 or _np.ndim(z) == 2: return compute_pupil_function(z) elif _np.ndim(z) == 1: return _np.array([compute_pupil_function(_) for _ in z])
def __init__(self, size, cx, cy, d): self.cx = float(cx) self.cy = float(cy) self.d = float(d) self.size = size self.ny, self.nx = size self.x_pxl, self.y_pxl = _np.meshgrid(_np.arange(self.nx), _np.arange(self.ny)) self.x_pxl -= int(self.cx) self.y_pxl -= int(self.cy) self.r_pxl = _msqrt(self.x_pxl**2 + self.y_pxl**2) self.r = 2.0 * self.r_pxl / d self.theta = _np.arctan2(self.y_pxl, self.x_pxl) self.x = 2.0 * self.x_pxl / d self.y = 2.0 * self.y_pxl / d
def retrievePF(self, dz, psf_diam): # an ultrasimplified version z_offset, zz = psf_zplane(self.PSF, dz, self.lcenter/3.2) # This should be the reason!!!! >_< A = self.PF.plane Mx, My = np.meshgrid(np.arange(self.nx)-self.nx/2., np.arange(self.nx)-self.nx/2.) r_pxl = _msqrt(Mx**2 + My**2) bk_inner = psf_diam bk_outer = (psf_diam+self.nx/2)/2 # updated on 05/15 hcyl = np.array(self.nz*[np.logical_and(r_pxl>=bk_inner, r_pxl<bk_outer)]) background = np.mean(self.PSF[hcyl]) print( " background = ", background) print( " z_offset = ", z_offset) PSF_sample = self.PSF zs = zz-z_offset complex_PF = self.PF.psf2pf(PSF_sample, zs, background, A, self.nIt) Pupil_final = _PupilFunction(complex_PF, self.PF) self.pf_complex = Pupil_final.complex self.pf_phase = unwrap_phase(Pupil_final.phase) self.pf_ampli = Pupil_final.amplitude
def background_reset(self, mask, psf_diam): ''' reset the background of the PSF mask is the outer diameter psf_diam is the inner diameter ''' Mx, My = np.meshgrid( np.arange(self.nx) - self.nx / 2., np.arange(self.nx) - self.nx / 2.) r_pxl = _msqrt(Mx**2 + My**2) bk_inner = psf_diam bk_outer = mask hcyl = np.array( self.nz * [np.logical_and(r_pxl >= bk_inner, r_pxl < bk_outer + 1)]) incyl = np.array(self.nz * [r_pxl < bk_outer]) background = np.mean(self.PSF[hcyl]) self.PSF[np.logical_not(incyl)] = background return background
def spatial_radial_coordinate_to_xy_slope(self, s): _mc = _msqrt((self.n * self.f / self.s)**2 - 1) return _np.real(_mc) - _np.imag(_mc)
def retrievePF(PSF, dx, dz, l, n, NA, f, nIt, bscale=1.0, neglect_defocus=True, invert=False, wavelengths=1, resetAmp=False, symmeterize=False): z_offset = 0 if neglect_defocus: # We try to estimate the axial position of the emitter # The coordinates of the brightest pixel cz, cx, cy = np.unravel_index(PSF.argmax(), PSF.shape) # Intensity trace along z i = PSF[:,cx,cy] # Get z positions nz = PSF.shape[0] upper = 0.5*(nz-1)*dz z = np.linspace(-upper, upper, nz) # Initial fit parameters b = np.mean((i[0],i[-1])) a = i.max() - b w = l/3.2 p0 = (a,0,w,b) def gaussian(z, a, z0, w, b): return a * np.exp(-(z-z0)**2/w) + b # Fit gaussian to axial intensity trace popt, pcov = optimize.curve_fit(gaussian, z, i, p0) # Where we think the emitter is axially located: z_offset = -1.0*popt[1] #Added on April 3, 2015 # plt.plot(z, i) # plt.plot(z, gaussian(z,*popt)) # plt.savefig('z_fit.png') nx,ny = PSF.shape[1:3] PF = pupil.Simulation(nx,dx,l,n,NA,f,wavelengths=wavelengths) # self, nx=256, dx=0.1, l=0.68, n=1.33, NA=1.27, f=3333.33, wavelengths=10, wavelength_halfmax=0.005 A = PF.plane Mx, My = np.meshgrid(np.arange(nx)-nx/2., np.arange(nx)-nx/2.) r_pxl = _msqrt(Mx**2 + My**2) hcyl = np.array(nz*[np.logical_and(r_pxl>=65, r_pxl<76)]) background = np.mean(PSF[hcyl])*bscale print "Finding PF..." print " Using parameters:" print " dz = ", dz print " background = ", background print " z_offset = ", z_offset complex_PF = PF.psf2pf(PSF, dz, background, A, nIt, z_offset, resetAmp=resetAmp,symmeterize=symmeterize) # def psf2pf(self, PSF, dz, mu, A, nIterations=10, z_offset=0, use_pyfftw=True, resetAmp=True, # symmeterize=False): if invert: complex_PF = abs(complex_PF) * np.exp(-1*1j*np.angle(complex_PF)) Pupil_final = _PupilFunction(complex_PF, PF) nz = PSF.shape[0] upper = 0.5*(nz-1)*dz z = np.linspace(-upper, upper, nz) psft = PF.pf2psf(complex_PF, z) # def psf2pf(self, PSF, dz, mu, A, nIterations=10, z_offset=0, use_pyfftw=True, resetAmp=True, # symmeterize=False): np.save('retrieved_psf', psft) return Pupil_final.phase
def spatial_radial_coordinate_to_xy_slope(self, s): # what does this one do? What's difference between s and self.s? # This is cot instead of tan. _mc = _msqrt((self.n*self.f/self.s)**2-1) return _np.real(_mc) - _np.imag(_mc)