lightpipes icon indicating copy to clipboard operation
lightpipes copied to clipboard

How to achieve the Zernike polynomials combination with different j_terms?

Open ltang320 opened this issue 5 years ago • 1 comments

I have a series of Zernike polynomials coefficients (j_terms form 1 ~36). However, I don't know how to achieve their combination by LightPipes.

Field2 = lp.Begin(10*lp.um,lamda,410)
for Noll in range(1, 36):
        (nz, mz) = lp.noll_to_zern(Noll)
        e_m = 2 if mz == 0 else 1
        A = 1
        #A = math.sqrt((2 * nz + 2) / e_m / math.pi)
        #print(e_m)
        Field2 = lp.Zernike(nz, mz, 10*lp.um, zernike_poly[Noll] * A, Field2)
phase2 = lp.PhaseUnwrap(phase2)

Screenshot 2020-11-27 at 18 11 33

I achieve it like this. But the phase is not as I imagined. It's very strange. Do you have any suggestions that how I can achieve this goal correctly?

(ldoyle edited code formatting)

ltang320 avatar Nov 27 '20 17:11 ltang320

Dear Vicky,

I think you just got the units wrong. In LightPipes the default units for Zernike polynomials is optical path length, i.e. [m]. This follows from the definition of the Zernike polynomials as a phase factor exp(1j*2*pi/lambda*Z). You have two options to solve this:

  1. scale the amplitude as desired, e.g. A=1*lamda for a 1 lambda = 2 Pi phase shift
  2. in the most recent LightPipes, you can specify alternative units in the Zernike function. Then, e.g. A=1 and units='lam' will also create a 2 Pi phase shift. Check my example below.
import numpy as np
import matplotlib.pyplot as plt

import LightPipes as lp


lamda = 800*lp.nm

zernike_poly = {i:1 for i in range(1,36)} #amplitude 1 for all indices

Field2 = lp.Begin(10*lp.um,lamda,410)
for Noll in range(1, 36):
        (nz, mz) = lp.noll_to_zern(Noll)
        e_m = 2 if mz == 0 else 1
        A = 1
        #A = math.sqrt((2 * nz + 2) / e_m / math.pi)
        #print(e_m)
        if lp.__version__ == '2.0.5': #or greater actually
            #latest LightPipes has inverted argument order for usability
            # and adds the optional "units" kwarg
            Field2 = lp.Zernike(Field2, nz, mz, 10*lp.um, zernike_poly[Noll] * A, units='lam')
        else:
            A = 1*lamda
            Field2 = lp.Zernike(nz, mz, 10*lp.um, zernike_poly[Noll] * A, Field2)
phase2 = lp.Phase(Field2)
phase2 = lp.PhaseUnwrap(phase2)

plt.imshow(phase2)

grafik

Some addition comments:

  • The weird diagonal pattern you are seeing comes from the internal workings of PhaseUnwrap and happens when the phase jump from 1 data point to the next is too large (~1Pi) in which case there is no definite direction of unwrapping. Try increasing A=1 to A=100 or A=1000 lambdas to see the effect in action. If the jumps are really too large for your case, you need to increase the sampling (more datapoints in same dimension).
  • You may already know this: The keyword lambda is reserved in Python. You may use it as a variable, but it's a very bad idea. Your lamda or alternatives like lam or labda are the safe ways to go.
  • When you post code, make sure to format it as code using 3 ticks before and after to help readability.
  • Your code was not a minimum working example since some variables and imports were undefined. I added them accordingly. In the future, best make sure the minimal script you post actually runs and produces the image you posted. In this case, I could not reconstruct the meaning of zernike_poly[Noll], therefore I assume it's 1 for all entries.

Let us know if this solves your problem. Best, Lenny

ldoyle avatar Nov 30 '20 15:11 ldoyle