pyoptics.interferometry¶
Interferometry: simulation of interferograms, phase-shifting interferometry (PSI) and Fourier-transform fringe analysis (Takeda).
Conventions¶
Fields vary as exp(i(k.r - omega*t)) (see the README). The test beam carries the phase phi = k*W of its wavefront error (or optical path difference) W, so a wavefront that runs ahead has phi > 0. Superposing it with a reference beam E_ref gives the two-beam interferogram
I = |E_test + E_ref*exp(i*delta)|**2
= I_0*(1 + gamma*cos(phi - delta)),
where phi = arg(E_test*conj(E_ref)) is the phase of the test beam relative to the reference and gamma the visibility (fringe contrast). A phase shift delta > 0 delays the reference by delta (its optical path gets longer by delta/k), so the fringes move towards larger phi. All algorithms here return phi, i.e. the phase of the test beam relative to the reference, with the sign convention of pyoptics: phase = +k*OPD. Note that many textbooks write cos(phi + delta) instead; their formulas then give -phi.
In reflection (Twyman-Green, Fizeau), the light passes the surface twice, so a surface height h gives phi = 2*k*h.
A tilt between the beams adds a linear carrier phase k_c.r = k_cx*x + k_cy*y (given here as a wavenumber in rad per length unit, like the k of utils.freq_grid()).
Image stacks follow the convention of pyoptics.processing: the last index numbers the images, i.e. frames[:, :, j] is the j-th phase-shifted frame (any number of leading dimensions is fine). Stacks can thus be passed through processing.Scaler and processing.CameraNoiser directly.
The phase-shifting algorithms return the wrapped phase in (-pi, pi] and the modulation gamma = b/a, where I = a + b*cos(phi - delta), i.e. the local fringe visibility (between 0 and 1 for physical intensities). Pixels without light (a = 0) get gamma = 0. Unwrap the phase with pyoptics.unwrap.
Contents¶
Simulation:
interferogram(): interferogram (or stack) from a phase map
interfere(): interferogram (or stack) from two complex fields
zernike_phase(): phase map from Fringe Zernike coefficients in waves
phase_shifts(): nominal or miscalibrated phase shifts
Phase-shifting algorithms:
psi_n_step(): least squares for N frames with arbitrary known shifts
psi_4_step(): four-bucket algorithm (shifts 0, pi/2, pi, 3*pi/2)
psi_carre(): Carré, four frames with an unknown but constant step
psi_hariharan(): Hariharan’s five-frame algorithm, insensitive to linear shift errors
shift_error_sensitivity(): rms phase error vs. phase shifter miscalibration
Single-frame analysis:
fourier_fringe_analysis(): Takeda’s Fourier-transform method for interferograms with a carrier
find_carrier(): locate the carrier sideband in the spectrum
Spherical surfaces (radius of curvature tests):
aplanatic_directions(): ray directions of a converging test wave
sphere_displacement_phase(): phase of a displaced test sphere
fit_sphere_displacement(): displacement of a test sphere from the null position (confocal or cat’s eye) from its measured phase
A sphere tested with a converging spherical wave (Fizeau transmission sphere, Twyman-Green with a diverger lens) gives a null interferogram in two positions: confocal, with its centre of curvature in the focus, and cat’s eye, with its vertex in the focus. The radius of curvature is the distance between the two, R = z_cat - z_confocal (signed as in pyoptics.raytrace, R > 0 for a centre of curvature in +z, i.e. downstream). Near either position, a small displacement d of the sphere changes the optical path of the ray with the direction n by d.n per pass, i.e. the phase by
phi = passes*k*(d_x*n_x + d_y*n_y + d_z*n_z),
tilt fringes for a lateral and power (defocus) fringes for an axial displacement. Fitting this model to the measured phase gives the null positions from slightly defocused interferograms. In cat’s eye, lateral displacements cancel to first order (the beam returns on the opposite side of the pupil).
References¶
J. H. Bruning, D. R. Herriott, J. E. Gallagher, D. P. Rosenfeld, A. D. White and D. J. Brangaccio, “Digital wavefront measuring interferometer for testing optical surfaces and lenses”, Appl. Opt. 13, 2693-2703 (1974).
J. E. Greivenkamp, “Generalized data reduction for heterodyne interferometry”, Opt. Eng. 23, 350-352 (1984).
P. Carré, “Installation et utilisation du comparateur photoélectrique et interférentiel du Bureau International des Poids et Mesures”, Metrologia 2, 13-23 (1966).
P. Hariharan, B. F. Oreb and T. Eiju, “Digital phase-shifting interferometry: a simple error-compensating phase calculation algorithm”, Appl. Opt. 26, 2504-2506 (1987).
M. Takeda, H. Ina and S. Kobayashi, “Fourier-transform method of fringe-pattern analysis for computer-based topography and interferometry”, J. Opt. Soc. Am. 72, 156-160 (1982).
K. Creath, “Phase-measurement interferometry techniques”, Prog. Opt. 26, 349-393 (1988).
Malacara (ed.), “Optical Shop Testing”, 3rd ed., Wiley (2007).
Functions
Directions of the rays of a converging spherical wave from an aplanatic transmission sphere (sine condition: sin(theta) = NA*rho). |
|
Locate the carrier, i.e. the +1 sideband of a fringe pattern. |
|
Least squares fit of the displacement of a test sphere from its null position to its unwrapped phase. |
|
Takeda's Fourier-transform method (Takeda, Ina, Kobayashi 1982) for a single interferogram with a carrier. |
|
Interferogram |
|
Two-beam interferogram I = I0*(1 + visibility*cos(phase + k_c.r - shift)) of a test phase. |
|
Phase shifts of a phase-shifting sequence, optionally with a miscalibrated phase shifter. |
|
Four-bucket algorithm for the shifts 0, pi/2, pi and 3*pi/2. |
|
Carré's algorithm: four frames with the shifts -3*alpha/2, -alpha/2, alpha/2, 3*alpha/2 and an unknown step alpha. |
|
Hariharan's five-frame algorithm (Hariharan, Oreb, Eiju 1987) for the shifts -2*alpha, -alpha, 0, alpha, 2*alpha with alpha = pi/2. |
|
Least-squares phase-shifting algorithm for N frames with known shifts. |
|
Rms phase error of a phase-shifting algorithm vs. the calibration error of the phase shifter. |
|
Phase passes*k*(d.n) of a test sphere displaced by d from its null position (first order in d), see the module docstring. |
|
Phase map 2*pi*passes*W of a wavefront W given by Fringe Zernike coefficients in waves. |
- pyoptics.interferometry.zernike_phase(x, y, coeffs, R_norm, indices=None, x0=0.0, y0=0.0, passes=1)[source]¶
Phase map 2*pi*passes*W of a wavefront W given by Fringe Zernike coefficients in waves.
- Parameters:
- x, yarrays
Linear coordinates of the grid.
- coeffssequence of floats
Fringe Zernike coefficients in waves (units of the wavelength).
- R_normfloat
Radius of the pupil (normalization radius of the Zernikes).
- indicessequence of ints, optional
Fringe indices that belong to coeffs. Defaults to 1, 2, …, len(coeffs).
- x0, y0floats, optional
Center of the pupil.
- passesint, optional
Number of passes through the test object, e.g. 2 for a surface measured in reflection (the coefficients then describe the surface height in waves).
- Returns:
- phasearray
Phase in radians, zero outside the pupil.
- maskarray of bool
Pupil mask.
- pyoptics.interferometry.phase_shifts(N, step=None, start=None, linear_error=0.0, quadratic_error=0.0)[source]¶
Phase shifts of a phase-shifting sequence, optionally with a miscalibrated phase shifter.
The nominal shifts are delta_j = start + j*step, j = 0, …, N - 1. A phase shifter with the relative linear calibration error epsilon_1 and a quadratic nonlinearity epsilon_2 produces
delta_j' = delta_j*(1 + epsilon_1) + epsilon_2*delta_j**2/pi,
the error being taken relative to the shifter’s zero position (delta = 0).
- Parameters:
- Nint
Number of frames.
- stepfloat, optional
Nominal phase step in radians. Defaults to 2*pi/N.
- startfloat, optional
Nominal first shift. Defaults to 0, i.e. the sequence 0, step, … Use start=-(N - 1)*step/2 for a sequence centered at 0 (as for Carré’s and Hariharan’s algorithms).
- linear_errorfloat, optional
Relative linear calibration error epsilon_1, e.g. 0.05 for a shifter that moves 5 % too far.
- quadratic_errorfloat, optional
Quadratic nonlinearity epsilon_2 (relative error of a shift of pi).
- Returns:
- shiftsarray of length N
- pyoptics.interferometry.interferogram(phase, x=None, y=None, visibility=1.0, carrier=(0.0, 0.0), shift=0.0, I0=1.0, noiser=None)[source]¶
Two-beam interferogram I = I0*(1 + visibility*cos(phase + k_c.r - shift)) of a test phase.
- Parameters:
- phasearray
Phase of the test beam relative to the reference in radians (e.g. from zernike_phase()).
- x, yarrays, optional
Linear coordinates of the grid; only needed for a carrier.
- visibilityfloat or array, optional
Fringe visibility gamma (may vary across the field).
- carriertuple of floats, optional
Carrier wavenumbers (k_cx, k_cy) in rad per length unit of a tilt between the beams, e.g. (2*pi/period, 0) for vertical fringes.
- shiftfloat or sequence of floats, optional
Phase shift(s) of the reference in radians. For a sequence, the result is a stack with the frame index last.
- I0float or array, optional
Mean intensity (bias).
- noisercallable, optional
Applied to the result, e.g. a processing.CameraNoiser (in that case, I0 is the mean number of photons per pixel).
- Returns:
- Iarray
Interferogram, or stack of interferograms of shape phase.shape + (len(shift),).
- pyoptics.interferometry.interfere(E_test, E_ref, shift=0.0)[source]¶
Interferogram
|E_test + E_ref*exp(i*shift)|**2of two fields.The algorithms of this module recover arg(E_test*conj(E_ref)) from it.
- Parameters:
- E_test, E_refarrays (or scalar for E_ref)
Complex fields of the test and reference beams.
- shiftfloat or sequence of floats, optional
Phase shift(s) of the reference beam. For a sequence, the result is a stack with the frame index last.
- Returns:
- Iarray
- pyoptics.interferometry.psi_n_step(frames, shifts=None, return_bias=False)[source]¶
Least-squares phase-shifting algorithm for N frames with known shifts.
Fits I_j = a + c*cos(delta_j) + s*sin(delta_j) per pixel (Bruning et al. 1974, Greivenkamp 1984), which gives phi = atan2(s, c) and b = sqrt(c**2 + s**2). For equally spaced shifts over one period, this is the classical N-bucket (synchronous detection) algorithm.
- Parameters:
- framesarray
Image stack, frame index last. At least 3 frames.
- shiftssequence of floats, optional
Phase shifts of the frames in radians. Defaults to 2*pi*j/N.
- return_biasbool, optional
If True, also return the bias a.
- Returns:
- phasearray
Wrapped phase phi in (-pi, pi].
- modulationarray
gamma = b/a.
- biasarray
Only if return_bias: the mean intensity a.
- pyoptics.interferometry.psi_4_step(frames)[source]¶
Four-bucket algorithm for the shifts 0, pi/2, pi and 3*pi/2.
The phase is
phi = atan2(I_2 - I_4, I_1 - I_3).
- Parameters:
- framesarray
Stack of 4 frames, frame index last.
- Returns:
- phase, modulationarrays
Wrapped phase and modulation gamma = b/a.
- pyoptics.interferometry.psi_carre(frames, return_step=False)[source]¶
Carré’s algorithm: four frames with the shifts -3*alpha/2, -alpha/2, alpha/2, 3*alpha/2 and an unknown step alpha.
With D_1 = I_1 - I_4 and D_2 = I_2 - I_3,
tan(alpha/2)**2 = (3*D_2 - D_1)/(D_1 + D_2), tan(phi) = sgn(-D_2)*sqrt(|(3*D_2 - D_1)*(D_1 + D_2)|) / (I_2 + I_3 - I_1 - I_4).
The step is estimated per pixel; the modulation uses the median step. The estimate of alpha (and with it the phase) is ill-conditioned where sin(phi) or cos(phi) is close to 0, so noise hurts more than with algorithms for known shifts. Works for 0 < alpha < pi, best near alpha = 110 degrees.
- Parameters:
- framesarray
Stack of 4 frames, frame index last.
- return_stepbool, optional
If True, also return the median of the estimated step alpha.
- Returns:
- phase, modulationarrays
Wrapped phase and modulation gamma = b/a.
- alphafloat
Only if return_step: the estimated phase step in radians.
- pyoptics.interferometry.psi_hariharan(frames, return_step=False)[source]¶
Hariharan’s five-frame algorithm (Hariharan, Oreb, Eiju 1987) for the shifts -2*alpha, -alpha, 0, alpha, 2*alpha with alpha = pi/2:
phi = atan2(2*(I_4 - I_2), 2*I_3 - I_1 - I_5).
A linear miscalibration alpha = pi/2*(1 + epsilon) only causes a phase error of second order in epsilon (compared to first order for the four bucket algorithm).
- Parameters:
- framesarray
Stack of 5 frames, frame index last.
- return_stepbool, optional
If True, also return the median of the step estimated from cos(alpha) = (I_5 - I_1)/(2*(I_4 - I_2)) (Hariharan’s calibration formula).
- Returns:
- phase, modulationarrays
Wrapped phase and modulation gamma = b/a.
- alphafloat
Only if return_step: the estimated phase step in radians.
- pyoptics.interferometry.shift_error_sensitivity(algorithm, linear_errors, quadratic_error=0.0, N=None, step=None, N_phase=360)[source]¶
Rms phase error of a phase-shifting algorithm vs. the calibration error of the phase shifter.
Frames with miscalibrated shifts (see phase_shifts()) of noise-free fringes with visibility 1 are evaluated for N_phase test phases evenly spread over one period; the rms of the wrapped phase error is returned for each linear error.
- Parameters:
- algorithmstr or callable
“4-step”, “carre”, “hariharan”, “n-step” (least squares with the nominal shifts, use N and step) or a callable f(frames) -> (phase, modulation) (then N and step are needed).
- linear_errorssequence of floats
Relative linear calibration errors epsilon_1.
- quadratic_errorfloat, optional
Quadratic nonlinearity epsilon_2 (see phase_shifts()).
- Nint, optional
Number of frames (“n-step” and callables).
- stepfloat, optional
Nominal phase step; defaults to 2*pi/N for “n-step” and callables.
- N_phaseint, optional
Number of test phases.
- Returns:
- rms_errorarray
Rms phase error in radians for each linear error.
- pyoptics.interferometry.find_carrier(I, x, y, exclude=None, mask=None)[source]¶
Locate the carrier, i.e. the +1 sideband of a fringe pattern.
Searches the spectral peak in the half plane k_x > 0 (or k_x = 0, k_y > 0) outside a disk around k = 0 and returns the centroid of the power spectrum
|S|**2in a disk of radius|k|/2around the current estimate k, starting at the peak and iterated until it settles. For a test phase without net tilt (e.g. defocus or astigmatism, which spread the sideband symmetrically), the centroid is the carrier; a tilt of the test phase is included in it.- Parameters:
- Iarray
Interferogram.
- x, yarrays
Linear coordinates.
- excludefloat, optional
Radius around k = 0 (in rad per length unit) that is ignored. Defaults to 3 frequency samples.
- maskarray of bool, optional
Region with fringes (e.g. the pupil). Without it, the edge of a pupil can outshine the sideband.
- Returns:
- carriertuple of floats
(k_cx, k_cy).
- pyoptics.interferometry.fourier_fringe_analysis(I, x, y, carrier=None, window='tophat', radius=None, mask=None, apodize=0.0, remove_carrier=True, return_sideband=False)[source]¶
Takeda’s Fourier-transform method (Takeda, Ina, Kobayashi 1982) for a single interferogram with a carrier.
The interferogram I = a + b*cos(phi + k_c.r) = a + c*exp(i k_c.r) + c.c., with c = b/2*exp(i*phi), has a sideband around k_c in its spectrum. A window around k_c isolates it; its inverse transform, multiplied with exp(-i k_c.r), gives c and hence phi and b. The bias a is the low-pass filtered interferogram (same window around k = 0).
The method needs the sideband to be separated from the zero order and the -1 order: the local fringe frequency
|grad phi|must stay below the window radius (by default|k_c|/2). A warning is issued if the window holds less than 90 % of the power in the half plane of the carrier, or if it reaches beyond the Nyquist frequency.- Parameters:
- Iarray
Interferogram.
- x, yarrays
Linear coordinates.
- carriertuple of floats, optional
Carrier (k_cx, k_cy) in rad per length unit. Found with find_carrier() if not given; then the tilt of the test phase is removed along with the carrier (remove a residual tilt with a fit if needed).
- windowstr or array, optional
“tophat” (default), “hann” or “gaussian” window around k_c, or a window array on the centered frequency grid of I. The tapered windows ring less but attenuate the outer parts of a broad sideband, which biases the modulation low; the bias is filtered with the same window around k = 0 (for a window array, the global mean is used).
- radiusfloat, optional
Window radius in rad per length unit. Defaults to
|k_c|/2.- maskarray of bool, optional
Region with fringes (e.g. the pupil). Outside, I is replaced by its mean inside before the transform, which reduces ringing at the edge, and the outputs are set to 0.
- apodizefloat, optional
Taper fraction of a separable Tukey window applied to I - mean(I) before the transform (0: no apodization, 1: Hann window). It reduces the ringing that the field edges cause in the interior, at the cost of larger errors in the tapered border. The modulation is corrected for it.
- remove_carrierbool, optional
If False, the carrier phase k_c.r is kept in the result.
- return_sidebandbool, optional
If True, also return the complex sideband c (after carrier removal).
- Returns:
- phasearray
Wrapped phase in (-pi, pi].
- modulationarray
gamma = b/a.
- sidebandarray
Only if return_sideband.
- pyoptics.interferometry.aplanatic_directions(x, y, R_pupil, NA, x0=0.0, y0=0.0)[source]¶
Directions of the rays of a converging spherical wave from an aplanatic transmission sphere (sine condition: sin(theta) = NA*rho).
The pupil point (x, y) (rho = r/R_pupil) belongs to the ray that converges towards the focus on the axis with the angle theta, i.e. it travels in +z and towards the axis.
- Parameters:
- x, yarrays
Linear coordinates of the grid (e.g. of the detector, on which the pupil is imaged).
- R_pupilfloat
Radius of the pupil on the grid.
- NAfloat
Numerical aperture of the test wave.
- x0, y0floats, optional
Centre of the pupil.
- Returns:
- directionsarray of shape (len(y), len(x), 3)
Unit vectors (n_x, n_y, n_z), NaN outside the pupil.
- pyoptics.interferometry.sphere_displacement_phase(directions, displacement, wavelength, passes=2)[source]¶
Phase passes*k*(d.n) of a test sphere displaced by d from its null position (first order in d), see the module docstring.
- Parameters:
- directionsarray of shape (…, 3)
Unit directions n of the test wave (e.g. from aplanatic_directions() or raytrace.System.ray_map()).
- displacement(d_x, d_y, d_z)
Displacement of the sphere in length units.
- wavelengthfloat
- passesint, optional
2 for a surface in reflection.
- Returns:
- phasearray
- pyoptics.interferometry.fit_sphere_displacement(phase, directions, wavelength, mask=None, lateral=True, passes=2)[source]¶
Least squares fit of the displacement of a test sphere from its null position to its unwrapped phase.
Fits phi = c_0 + passes*k*(d.n) (see sphere_displacement_phase()). The axial component d_z from the confocal and the cat’s eye interferograms gives the null positions and the radius of curvature: with the stage positions z_1 (near confocal) and z_2 (near cat’s eye), R = (z_2 - d_z2) - (z_1 - d_z1).
- Parameters:
- phasearray
Unwrapped phase of the test beam relative to the reference.
- directionsarray of shape phase.shape + (3,)
Unit directions of the test wave at each pixel.
- wavelengthfloat
- maskarray of bool, optional
Pixels to use (default: all with finite phase and directions).
- lateralbool, optional
Fit the lateral displacement (d_x, d_y) as well. In cat’s eye, set it to False and fit tilt terms instead (lateral displacements do not show there; a tilt of the reference does).
- passesint, optional
- Returns:
- displacementarray (d_x, d_y, d_z)
(d_x, d_y are 0 for lateral=False.)
- residualarray
phase minus the fit (0 outside the mask).