Niccko commited on
Commit
87627fc
·
1 Parent(s): 0deb2e3

dataset generation

Browse files
Files changed (5) hide show
  1. .gitignore +2 -1
  2. fraunhofer_test.py +27 -21
  3. generate_dataset.py +113 -0
  4. optic.py +129 -0
  5. zern_generator.py +28 -23
.gitignore CHANGED
@@ -1,3 +1,4 @@
1
  venv
2
  .idea
3
- __pycache__
 
 
1
  venv
2
  .idea
3
+ __pycache__
4
+ screens
fraunhofer_test.py CHANGED
@@ -1,7 +1,8 @@
1
  import matplotlib.pyplot as plt
 
2
  import numpy as np
3
  import math
4
- from scipy.fft import fft2, fftshift, ifftshift
5
  from zern_generator import Zernike
6
 
7
 
@@ -17,40 +18,45 @@ class Fraunhofer:
17
  self.span = (-(10 ** -2), 10 ** -2)
18
  self.wavelength = 500 * 10 ** -9
19
  self.k = 2*math.pi/self.wavelength
20
-
21
  self.zernike = zernike
22
-
23
- def _denoise(screen: np.ndarray):
24
- return np.where(np.abs(screen) < 1e-4, 0, screen)
25
-
26
  def get_focal_point(self,input_screen):
27
- phase_mul = ((np.exp(1j*self.k*self.distance) * np.exp(1j*self.k*(self.zernike.xv**2 + self.zernike.yv**2)/(2*self.distance)))/(1j*self.wavelength*self.distance))
28
- U_fraun = fft2(ifftshift(input_screen), overwrite_x=True)
29
- U_fraun = fftshift(U_fraun * phase_mul)
 
 
 
 
 
 
30
 
31
 
32
- I_fraun = np.abs(U_fraun)**2
33
- return I_fraun / np.max(I_fraun)
34
-
35
-
36
  def generate_diff(self, defocus_amount, *zernikes):
37
  zernikes = list(zernikes)
38
  if len(zernikes) < 5:
39
  zernikes += [0] * (5-len(zernikes))
40
-
41
 
42
-
43
  wf = self.zernike.generate_zern_screen([1], self.radius)
44
-
45
 
46
  f_plus = self.zernike.generate_zern_screen(self.zernike.add_defocus(defocus_amount,zernikes), norm_radius=self.radius)
47
  f_minus = self.zernike.generate_zern_screen(self.zernike.add_defocus(-defocus_amount,zernikes), norm_radius=self.radius)
48
 
49
- focal_image_plus = denoise(self.get_focal_point(wf*np.exp(f_plus * 1j)))
50
- focal_image_minus = denoise(self.get_focal_point(wf*np.exp(f_minus * 1j)))
 
 
 
 
 
 
 
 
 
51
 
52
- return focal_image_plus, focal_image_minus, focal_image_plus-focal_image_minus
53
-
54
 
55
 
56
-
 
1
  import matplotlib.pyplot as plt
2
+ import cupy as cp
3
  import numpy as np
4
  import math
5
+ from cupyx.scipy.fft import fft2, fftshift, ifftshift
6
  from zern_generator import Zernike
7
 
8
 
 
18
  self.span = (-(10 ** -2), 10 ** -2)
19
  self.wavelength = 500 * 10 ** -9
20
  self.k = 2*math.pi/self.wavelength
21
+
22
  self.zernike = zernike
23
+
24
+ # def _denoise(screen: np.ndarray):
25
+ # return np.where(np.abs(screen) < 1e-4, 0, screen)
26
+
27
  def get_focal_point(self,input_screen):
28
+ input_screen_gpu = cp.asarray(input_screen)
29
+ #phase_mul = ((cp.exp(1j*self.k*self.distance) * cp.exp(1j*self.k*(self.zernike.xv**2 + self.zernike.yv**2)/(2*self.distance)))/(1j*self.wavelength*self.distance))
30
+ U_fraun = fft2(ifftshift(input_screen_gpu), overwrite_x=True)
31
+ #U_fraun = fftshift(U_fraun * phase_mul)
32
+ U_fraun = fftshift(U_fraun)
33
+
34
+
35
+ I_fraun = cp.abs(U_fraun)**2
36
+ return I_fraun / cp.max(I_fraun)
37
 
38
 
 
 
 
 
39
  def generate_diff(self, defocus_amount, *zernikes):
40
  zernikes = list(zernikes)
41
  if len(zernikes) < 5:
42
  zernikes += [0] * (5-len(zernikes))
 
43
 
 
44
  wf = self.zernike.generate_zern_screen([1], self.radius)
 
45
 
46
  f_plus = self.zernike.generate_zern_screen(self.zernike.add_defocus(defocus_amount,zernikes), norm_radius=self.radius)
47
  f_minus = self.zernike.generate_zern_screen(self.zernike.add_defocus(-defocus_amount,zernikes), norm_radius=self.radius)
48
 
49
+ wf_c = cp.asarray(wf)
50
+ f_plus_c = cp.asarray(f_plus)
51
+ f_minus_c = cp.asarray(f_minus)
52
+ focal_image_plus = denoise(self.get_focal_point(wf_c*cp.exp(f_plus_c * 1j)))
53
+ focal_image_minus = denoise(self.get_focal_point(wf_c*cp.exp(f_minus_c * 1j)))
54
+
55
+ return (
56
+ cp.asnumpy(focal_image_plus),
57
+ cp.asnumpy(focal_image_minus),
58
+ cp.asnumpy(focal_image_plus - focal_image_minus)
59
+ )
60
 
 
 
61
 
62
 
 
generate_dataset.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from zern_generator import Zernike
2
+ from PIL import Image
3
+ from fraunhofer_test import Fraunhofer
4
+ import cupy as cp
5
+ import matplotlib.pyplot as plt
6
+ import itertools
7
+ from time import time
8
+ from pprint import pprint
9
+ import os
10
+ from multiprocessing import Pool, cpu_count
11
+ import numpy as np
12
+
13
+ zernike = Zernike()
14
+ zernike.set_image_params(npix=2 ** 8)
15
+ fraunhofer = Fraunhofer(zernike)
16
+
17
+
18
+ def generate_image(z):
19
+
20
+
21
+ start_time = time()
22
+ arr = fraunhofer.generate_diff(1, 0, *z)
23
+ a = cp.min(arr[2])
24
+ b = cp.max(arr[2])
25
+ img_arr = ((arr[2] - a) / (b - a) * 255).astype(cp.uint8)
26
+ img = Image.fromarray(np.stack([img_arr] * 3, axis=-1), mode='RGB')
27
+ generation_end_time = time()
28
+ if True:
29
+ coeffs = '_'.join([f'{x:.2f}' for x in z[2:]])
30
+ if not os.path.exists(f'./screens_gpu/{z[0]}/{z[1]}'):
31
+ try:
32
+ os.makedirs(f'./screens_gpu/{z[0]}/{z[1]}')
33
+ except:
34
+ pass
35
+ filename = f'./screens_gpu/{z[0]}/{z[1]}/{coeffs}_.png'
36
+ img.save(filename)
37
+ save_end_time = time()
38
+ else:
39
+ plt.imshow(img)
40
+ plt.colorbar()
41
+ plt.show()
42
+ return generation_end_time - start_time, save_end_time - generation_end_time
43
+
44
+
45
+ def generate_dataset(zs):
46
+ c = 0
47
+ window_size = 50
48
+ times = []
49
+ start_time = time()
50
+ total_images = len(zs)
51
+ num_workers = 24
52
+ if num_workers is None:
53
+ num_workers = cpu_count()
54
+
55
+ with Pool(num_workers) as pool:
56
+ results = []
57
+
58
+ for result in pool.imap_unordered(generate_image, zs):
59
+ c += 1
60
+
61
+ results.append(result)
62
+
63
+ current_time = time()
64
+ iteration_time = current_time - start_time
65
+ times.append(iteration_time)
66
+ if len(times) > window_size:
67
+ times.pop(0)
68
+ avg_iteration_time = sum(times) / len(times)
69
+
70
+ # if c % 10 == 0:
71
+ # os.system('cls')
72
+ # print(f"Generated {c}/{total_images}")
73
+ # elapsed_time = current_time - start_time
74
+ # print(f"Elapsed time: {elapsed_time:.2f} s")
75
+ # remaining_images = total_images - c
76
+ # estimated_remaining_time = avg_iteration_time * remaining_images
77
+ # print(f"Estimated remaining time: {estimated_remaining_time:.2f} s")
78
+ for result in results:
79
+ result.get()
80
+
81
+
82
+ def generate_dataset_gpu(zs):
83
+ c = 0
84
+ window_size = 50
85
+ times = []
86
+ start_time = time()
87
+ total_images = len(zs)
88
+ num_workers = 24
89
+ if num_workers is None:
90
+ num_workers = cpu_count()
91
+
92
+ for z in zs:
93
+
94
+ generate_image(z)
95
+
96
+
97
+ if __name__ == '__main__':
98
+ rng = (-5, 5)
99
+ step = 0.5
100
+ points = tuple(np.linspace(*rng, int((rng[1] - rng[0]) / step + 1)))
101
+ points_as_tuples = tuple(float(x) for x in points)
102
+
103
+ zernikes = np.array(list(set(itertools.product(points_as_tuples, repeat=3))))
104
+
105
+ # print("Total images: ", len(zernikes))
106
+ # total_gen_time, total_save_time = 0, 0
107
+ # for z in zernikes:
108
+ # gen_time, save_time = generate_image(z)
109
+ # total_save_time += save_time
110
+ # total_gen_time += gen_time
111
+ # print("Average generation time: ", total_gen_time/len(zernikes))
112
+ # print("Average save time: ", total_save_time/len(zernikes))
113
+ generate_dataset(zernikes)
optic.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import astropy.units as u
2
+ import pyoptica as po
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ import math
6
+ import os
7
+ from scipy.fft import fft2, ifft2, fftshift, ifftshift
8
+ import aotools.opticalpropagation as op
9
+ import scipy.special as sc
10
+ import time
11
+ import multiprocessing
12
+ from multiprocessing import Pool
13
+ from zern_generator import generate_zern_wavefront, generate_zern_wavefront_fig
14
+ from scipy.ndimage import zoom
15
+
16
+ fig, (ax1, ax2) = plt.subplots(2,1)
17
+
18
+
19
+ #f2.show()
20
+
21
+ f = 100 * 10 ** -3 # Форкусное расстояние линзы
22
+
23
+
24
+
25
+ radius = 5 * 10 ** -3 # Радиус линзы
26
+ span = (-(2*radius), 2*radius)
27
+ lens = po.ThinLens(radius, f)
28
+
29
+ z = 100 * 10 ** -3 # Расстояние
30
+
31
+ wavelength = 500 * 10 ** -9 # Длина волны
32
+ pixel_scale = 250 * 10 ** -4 # Размер пикселя
33
+ npix = 2**9 # Размер сетки
34
+
35
+
36
+ ddx = np.linspace(*span, npix)
37
+ ddy = np.linspace(*span, npix)
38
+ xv, yv = np.meshgrid(ddx, ddy)
39
+ f2 = generate_zern_wavefront(xv, yv, [0,0,0,1,0,0], norm_radius=radius)
40
+ dist_from_center = np.sqrt(xv**2 + yv**2)
41
+ mask = dist_from_center <= radius
42
+ f2[mask==0] = 0
43
+ f2[np.isnan(f2)] = 0
44
+ k = 2*math.pi/wavelength
45
+
46
+
47
+ wf = np.zeros((npix, npix), dtype=np.complex64)
48
+
49
+ wf[mask] = 1 + 0j
50
+
51
+ wf += f2 * 1j
52
+
53
+ wf = fftshift(fft2(wf))
54
+
55
+ phase_mul = ((np.exp(1j*k*z) * np.exp(1j*k*(xv**2 + yv**2)/(2*z)))/(1j*wavelength*z))
56
+
57
+ #wf = wf * phase_mul
58
+ wf= np.abs(wf)**2
59
+ #wf = np.log10(wf)
60
+
61
+ crop = 20
62
+ center = npix//2
63
+ vx = np.linspace(*span, npix)
64
+ ax1.imshow(wf, extent = (*span, *span), cmap="gray")
65
+ #ax2.plot(vx, wf[npix//2])
66
+ ax2.imshow(f2, extent = (*span, *span),cmap="gray")
67
+ print(np.ptp(wf))
68
+ plt.show()
69
+
70
+
71
+
72
+
73
+
74
+ # amp = np.array(wf.amplitude)
75
+
76
+ # a = fftshift(fft2(wf.amplitude))
77
+ # a = np.abs(a)**2
78
+ # print(a)
79
+ # ax.imshow(a, extent=(-npix*pixel_scale/2, npix*pixel_scale/2,-npix*pixel_scale/2, npix*pixel_scale/2))
80
+ # plt.show()
81
+
82
+
83
+ # # Двойной интеграл из 4.13
84
+ # def calc_point_fourier(x,y, uxy):
85
+ # res = 0 + 0j
86
+ # for x0 in range(npix):
87
+ # x_r = to_real(x0)
88
+ # for y0 in range(npix):
89
+ # y_r = to_real(y0)
90
+ # res += uxy * np.exp(-1j*k*(x*x_r + y*y_r)/z)
91
+ # return res
92
+
93
+
94
+ # # Считает точку по 4.13
95
+ # def calc_point(x,y):
96
+ # x_r = to_real(x)
97
+ # y_r = to_real(y)
98
+ # if y%100 == 1:
99
+ # os.system('cls')
100
+ # print(f"{npix*x + y}/{npix**2}")
101
+ # return ((np.exp(1j*k*z) * np.exp(1j*k*(x_r**2 + y_r**2)/(2*z)))/(1j*wavelength*z)) * calc_point_fourier(x_r, y_r, amp[x][y])
102
+
103
+
104
+
105
+
106
+ # if __name__ == "__main__":
107
+ # pairs = []
108
+ # for x in range(npix):
109
+ # for y in range(npix):
110
+ # pairs.append((x,y))
111
+ # multiprocessing.freeze_support()
112
+ # with Pool(20) as p:
113
+ # res = p.starmap_async(calc_point, pairs)
114
+ # a = res.get()
115
+ # a = np.abs(a)**2
116
+ # a = a.reshape((npix, npix))
117
+ # a /= np.max(a)
118
+ # ax.imshow(a, extent=(-npix*pixel_scale/2, npix*pixel_scale/2,-npix*pixel_scale/2, npix*pixel_scale/2))
119
+ # plt.show()
120
+
121
+
122
+
123
+
124
+
125
+
126
+ try:
127
+ pass
128
+ except KeyError:
129
+
zern_generator.py CHANGED
@@ -2,6 +2,7 @@ import math
2
 
3
  import matplotlib.pyplot as plt
4
  import numpy as np
 
5
  from zernike import RZern
6
  import utils
7
 
@@ -9,7 +10,6 @@ SAMPLE_SIZE = 256
9
  span = (-1, 1)
10
 
11
 
12
-
13
  class Zernike:
14
  def __init__(self):
15
  self.set_image_params()
@@ -29,33 +29,37 @@ class Zernike:
29
  "P-SPHERICAL",
30
  "V-SC-ASTIGMATISM",
31
  "V-QUADRAFOIL",
32
-
33
  # "V-SC-COMA",
34
  # "H-SC-COMA",
35
  # "V-SC-TREFOIL",
36
  # "O-SC-TREFOIL",
37
  # "O-PENTAFOIL",
38
  # "V-PENTAFOIL"
39
- ]
40
-
41
- def set_image_params(self, span = (-(10 ** -2), 10 ** -2), npix = 2**8):
42
  self.span = span
43
  self.npix = npix
44
  ddx = np.linspace(*self.span, self.npix)
45
  ddy = np.linspace(*self.span, self.npix)
46
- self.xv, self.yv = np.meshgrid(ddx, ddy)
47
-
48
- def generate_zern_screen(self, zerns, norm_radius = 1):
 
 
 
49
  zerns = self._osa_to_noll_list(zerns)
50
  num_zerns = len(zerns)
51
  mask = utils.circle_mask(self.xv, self.yv, norm_radius)
52
-
53
-
54
  degree = math.ceil((-3 + math.sqrt(8 * num_zerns + 1)) / 2)
55
  zerns = list(zerns) + [0] * int((((degree + 1) * (degree + 2) / 2) - len(zerns)))
56
  cart = RZern(degree)
57
- cart.make_cart_grid(self.xv/norm_radius, self.yv/norm_radius)
58
- screen = np.array(cart.eval_grid(np.array(zerns), matrix=True))
 
 
59
  screen[mask == 0] = 0
60
  screen[np.isnan(screen)] = 0
61
  return screen
@@ -70,17 +74,17 @@ class Zernike:
70
  fig_2d.colorbar(a)
71
  plt.show()
72
  return fig_2d, fig_3d
73
-
74
  def add_defocus(self, defocus_value, zernikes):
 
75
  zernikes = list(zernikes)
76
  if len(zernikes) < 5:
77
- zernikes += [0] * (5-len(zernikes))
78
  zernikes[4] += defocus_value
79
  return zernikes
80
 
81
-
82
  def generate_batch(self, resolution):
83
- #term = ["PISTON","V-TILT","H-TILT","DEFOCUS","O-PR-ASTIGMATISM","V-PR-ASTIGMATISM","V-PR-COMA","H-PR-COMA","V-TREFOIL","O-TREFOIL","P-SPHERICAL","O-SC-ASTIGMATISM","V-SC-ASTIGMATISM","V-QUADRAFOIL","O-QUADRAFOIL","V-SC-COMA","H-SC-COMA","V-SC-TREFOIL","O-SC-TREFOIL","O-PENTAFOIL","V-PENTAFOIL"]
84
  fig = plt.figure()
85
  fig.set_figwidth(30)
86
  fig.set_figheight(20)
@@ -92,15 +96,14 @@ class Zernike:
92
 
93
  zern = [0] * (zern_index - 1) + [1]
94
  ax = plt.subplot2grid((5, 6), (i, j))
95
- Phi = self.generate_zern_screen(zern, norm_radius=self.span[1]/2)
96
- ax.set_title(f" \n Zernike {zern_index-1} — {self.term[zern_index-1]} ")
97
  ax.imshow(Phi, extent=(*span, *span), cmap='gray')
98
 
99
  zern_index += 1
100
  plt.show()
101
  return fig
102
 
103
-
104
  def _osa_to_mn(self, idx):
105
  n = math.ceil((-3 + math.sqrt((9 + 8 * idx))) / 2)
106
  m = 2 * idx - n * (n + 2)
@@ -122,7 +125,6 @@ class Zernike:
122
  j = first + second + third
123
  return j
124
 
125
-
126
  def _osa_to_noll(self, idx):
127
  return self._mn_to_noll(*self._osa_to_mn(idx))
128
 
@@ -130,8 +132,11 @@ class Zernike:
130
  indexed_zerns = sorted(map(lambda x: (self._osa_to_noll(x[0]), x[1]), enumerate(zernikes)), key=lambda x: x[0])
131
  used_indeces = [x for x, _ in indexed_zerns]
132
  mn, mx = min(used_indeces), max(used_indeces)
133
- missing_indeces = set(range(mn, mx+1)) - set(used_indeces)
134
  return [x[1] for x in list(sorted(indexed_zerns + [(x, 0) for x in missing_indeces]))]
135
 
136
- term = ["PISTON","V-TILT","H-TILT","DEFOCUS","O-PR-ASTIGMATISM","V-PR-ASTIGMATISM","V-PR-COMA","H-PR-COMA","V-TREFOIL","O-TREFOIL","P-SPHERICAL","O-SC-ASTIGMATISM","V-SC-ASTIGMATISM","V-QUADRAFOIL","O-QUADRAFOIL","V-SC-COMA","H-SC-COMA","V-SC-TREFOIL","O-SC-TREFOIL","O-PENTAFOIL","V-PENTAFOIL"]
137
- print(Zernike()._osa_to_noll_list(list(range(0,15))))
 
 
 
 
2
 
3
  import matplotlib.pyplot as plt
4
  import numpy as np
5
+ import cupy as cp
6
  from zernike import RZern
7
  import utils
8
 
 
10
  span = (-1, 1)
11
 
12
 
 
13
  class Zernike:
14
  def __init__(self):
15
  self.set_image_params()
 
29
  "P-SPHERICAL",
30
  "V-SC-ASTIGMATISM",
31
  "V-QUADRAFOIL",
32
+
33
  # "V-SC-COMA",
34
  # "H-SC-COMA",
35
  # "V-SC-TREFOIL",
36
  # "O-SC-TREFOIL",
37
  # "O-PENTAFOIL",
38
  # "V-PENTAFOIL"
39
+ ]
40
+
41
+ def set_image_params(self, span=(-(10 ** -2), 10 ** -2), npix=2 ** 8):
42
  self.span = span
43
  self.npix = npix
44
  ddx = np.linspace(*self.span, self.npix)
45
  ddy = np.linspace(*self.span, self.npix)
46
+ self.xv, self.yv = np.meshgrid(ddx, ddy)
47
+
48
+ def generate_zern_screen(self, zerns, norm_radius=1):
49
+
50
+ if isinstance(zerns, cp.ndarray):
51
+ zerns = zerns.get()
52
  zerns = self._osa_to_noll_list(zerns)
53
  num_zerns = len(zerns)
54
  mask = utils.circle_mask(self.xv, self.yv, norm_radius)
55
+
 
56
  degree = math.ceil((-3 + math.sqrt(8 * num_zerns + 1)) / 2)
57
  zerns = list(zerns) + [0] * int((((degree + 1) * (degree + 2) / 2) - len(zerns)))
58
  cart = RZern(degree)
59
+
60
+ cart.make_cart_grid(self.xv / norm_radius, self.yv / norm_radius)
61
+
62
+ screen = np.array(cart.eval_grid(np.array(zerns), matrix=True))
63
  screen[mask == 0] = 0
64
  screen[np.isnan(screen)] = 0
65
  return screen
 
74
  fig_2d.colorbar(a)
75
  plt.show()
76
  return fig_2d, fig_3d
77
+
78
  def add_defocus(self, defocus_value, zernikes):
79
+
80
  zernikes = list(zernikes)
81
  if len(zernikes) < 5:
82
+ zernikes += [0] * (5 - len(zernikes))
83
  zernikes[4] += defocus_value
84
  return zernikes
85
 
 
86
  def generate_batch(self, resolution):
87
+ # term = ["PISTON","V-TILT","H-TILT","DEFOCUS","O-PR-ASTIGMATISM","V-PR-ASTIGMATISM","V-PR-COMA","H-PR-COMA","V-TREFOIL","O-TREFOIL","P-SPHERICAL","O-SC-ASTIGMATISM","V-SC-ASTIGMATISM","V-QUADRAFOIL","O-QUADRAFOIL","V-SC-COMA","H-SC-COMA","V-SC-TREFOIL","O-SC-TREFOIL","O-PENTAFOIL","V-PENTAFOIL"]
88
  fig = plt.figure()
89
  fig.set_figwidth(30)
90
  fig.set_figheight(20)
 
96
 
97
  zern = [0] * (zern_index - 1) + [1]
98
  ax = plt.subplot2grid((5, 6), (i, j))
99
+ Phi = self.generate_zern_screen(zern, norm_radius=self.span[1] / 2)
100
+ ax.set_title(f" \n Zernike {zern_index - 1} — {self.term[zern_index - 1]} ")
101
  ax.imshow(Phi, extent=(*span, *span), cmap='gray')
102
 
103
  zern_index += 1
104
  plt.show()
105
  return fig
106
 
 
107
  def _osa_to_mn(self, idx):
108
  n = math.ceil((-3 + math.sqrt((9 + 8 * idx))) / 2)
109
  m = 2 * idx - n * (n + 2)
 
125
  j = first + second + third
126
  return j
127
 
 
128
  def _osa_to_noll(self, idx):
129
  return self._mn_to_noll(*self._osa_to_mn(idx))
130
 
 
132
  indexed_zerns = sorted(map(lambda x: (self._osa_to_noll(x[0]), x[1]), enumerate(zernikes)), key=lambda x: x[0])
133
  used_indeces = [x for x, _ in indexed_zerns]
134
  mn, mx = min(used_indeces), max(used_indeces)
135
+ missing_indeces = set(range(mn, mx + 1)) - set(used_indeces)
136
  return [x[1] for x in list(sorted(indexed_zerns + [(x, 0) for x in missing_indeces]))]
137
 
138
+
139
+ term = ["PISTON", "V-TILT", "H-TILT", "DEFOCUS", "O-PR-ASTIGMATISM", "V-PR-ASTIGMATISM", "V-PR-COMA", "H-PR-COMA",
140
+ "V-TREFOIL", "O-TREFOIL", "P-SPHERICAL", "O-SC-ASTIGMATISM", "V-SC-ASTIGMATISM", "V-QUADRAFOIL", "O-QUADRAFOIL",
141
+ "V-SC-COMA", "H-SC-COMA", "V-SC-TREFOIL", "O-SC-TREFOIL", "O-PENTAFOIL", "V-PENTAFOIL"]
142
+ print(Zernike()._osa_to_noll_list(list(range(0, 15))))