Openholo  v4.1
Open Source Digital Holographic Library
PointCloud_multicore.py
Go to the documentation of this file.
1 import numpy as np
2 import matplotlib.pyplot as plt
3 import plyfile
4 from numba import njit
5 from concurrent.futures import ProcessPoolExecutor
6 import multiprocessing
7 
8 
9 # unit parameters
10 mm = 1e-3
11 um = mm * mm
12 nm = um * mm
13 
14 
15 @njit(nogil=True, cache=True)
16 def k(wvl):
17  return (np.pi * 2) / wvl
18 
19 
20 @njit(nogil=True)
21 def h_RS(x1, y1, z1, x2, y2, z2, wvl, pp):
22  r = np.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2))
23  t = (wvl * r) / (2 * pp) # anti alliasing condition
24  if (x1 - t < x2 < x1 + t) and (y1 - t < y2 < y1 + t):
25  h_r = np.sin(k(wvl) * r) / r ** 2
26  h_i = np.cos(k(wvl) * r) / r ** 2
27  else:
28  h_r = 0
29  h_i = 0
30  return h_r, h_i
31 
32 
33 @njit(nogil=True)
34 def h_Frsn(x1, y1, z1, x2, y2, z2, wvl, pp):
35  """impulse response function of Fresnel propagation method"""
36  z = z2 - z1
37  r = ((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) / (2*z)
38  t = (wvl * z) / (2 * pp)
39  if (x1 - t < x2 < x1 + t) and (y1 - t < y2 < y1 + t): # anti aliasing
40  h_r = np.cos(k(wvl) * r)
41  h_i = np.sin(k(wvl) * r)
42  else:
43  h_r = 0
44  h_i = 0
45  return h_r, h_i
46 
47 
48 @njit(nogil=True)
49 def Conv(x1, y1, z1, z2, amp, h, w, pp, wvl, method):
50  ch_r = np.zeros((h, w))
51  ch_i = np.zeros((h, w))
52  for i in range(h):
53  for j in range(w):
54  x2 = (j - w / 2) * pp
55  y2 = (i - h / 2) * pp
56  if method == 'RS':
57  ch_r[i, j], ch_i[i, j] = h_RS(x1, y1, z1, x2, y2, z2, wvl, pp)
58  else:
59  ch_r[i, j], ch_i[i, j] = h_Frsn(x1, y1, z1, x2, y2, z2, wvl, pp)
60  return (ch_r + 1j * ch_i) * amp
61 
62 
64  """
65  Get Fringe pattern by Point Cloud data(.ply)
66 
67  Parameters
68  plypath : .ply file path
69  angle : phase shift angle
70  Red, Green, Blue : wavelength of RGB color
71  scale : scaling factor
72 
73  http://openholo.org/
74  """
75  def __init__(self, plypath, method='RS', propagation_distance=1, angleX=0, angleY=0, Red=639*nm, Green=525*nm, Blue=463*nm,
76  SLM_width=3840, SLM_height=2160, scaleXY=0.03, scaleZ=0.25, pixel_pitch=3.6*um, multicore=True):
77  self.z = propagation_distance # Propagation distance
78  self.methods = method
79  self.thetaX = angleX * (np.pi / 180)
80  self.thetaY = angleY * (np.pi / 180)
81  self.wvl_R = Red
82  self.wvl_G = Green
83  self.wvl_B = Blue
84  self.w = SLM_width
85  self.h = SLM_height
86  self.pp = pixel_pitch
87  self.scaleXY = scaleXY
88  self.scaleZ = scaleZ
89  with open(plypath, 'rb') as f:
90  self.plydata = plyfile.PlyData.read(f)
91  self.plydata = np.array(self.plydata.elements[1].data)
92  if multicore:
93  self.num_cpu = multiprocessing.cpu_count()
94  else:
95  self.num_cpu = 1
96  self.num_point = [i for i in range(len(self.plydata))]
97  self.num_point = np.array(self.num_point)
98 
99 
100  def Cal(self, n, color='red'):
101  """Convolution"""
102  if color == 'green':
103  wvl = self.wvl_G
104  elif color == 'blue':
105  wvl = self.wvl_B
106  else:
107  wvl = self.wvl_R
108  x0 = (self.plydata['x'][n] + self.z * np.tan(self.thetaX)) * self.scaleXY
109  y0 = (self.plydata['y'][n] + self.z * np.tan(self.thetaY)) * self.scaleXY
110  z0 = self.plydata['z'][n] * self.scaleZ
111  amp = self.plydata[color][n] * (self.z / wvl)
112  ch = Conv(x0, y0, z0, self.z, amp, self.h, self.w, self.pp, wvl, self.methods)
113  print(self.methods, 'methods ', n, ' th point ', color, ' Done')
114  return ch
115 
116  def Conv_R(self, n):
117  return self.Cal(n, 'red')
118 
119  def Conv_G(self, n):
120  return self.Cal(n, 'green')
121 
122  def Conv_B(self, n):
123  return self.Cal(n, 'blue')
124 
125  def CalHolo(self, color='red'):
126  """Calculate hologram"""
127  if color == 'green':
128  func = self.Conv_G
129  elif color == 'blue':
130  func = self.Conv_B
131  else:
132  func = self.Conv_R
133  print(self.num_cpu, " core Ready")
134  ch = np.zeros((self.h, self.w), dtype='complex128')
135  count = np.split(self.num_point, [i * self.num_cpu for i in range(1, len(self.plydata) // self.num_cpu)])
136  print(count)
137  for n in count:
138  with ProcessPoolExecutor(self.num_cpu) as ex:
139  cache = [result for result in ex.map(func, list(n))]
140  cache = np.asarray(cache)
141  print(n, 'steps done')
142  for j in range(len(n)):
143  ch += cache[j, :, :]
144  return ch
145 
146  # functions for encoding
147  def fft(self, f):
148  return np.fft.fftshift(np.fft.fft2(np.fft.fftshift(f)))
149 
150  def ifft(self, f):
151  return np.fft.fftshift(np.fft.ifft2(np.fft.fftshift(f)))
152 
153  def SSB(self, ch):
154  """single side band encoding"""
155  height, width = ch.shape
156  a = np.zeros((height, width), dtype='complex128')
157  CH = self.fft(ch) # fourier transformed image
158  CH = CH[height // 4: (height * 3) // 4, :]
159  a[0:height // 2, :] = CH
160  a[height // 2:, :] = np.conj(CH)
161  return self.ifft(a)
162 
163  def normalize(self, arr, type='angle'):
164  """normalize"""
165  if type == 'phase':
166  arrin = np.copy(np.imag(arr))
167  elif type == 'real':
168  arrin = np.copy(np.real(arr))
169  elif type == 'angle':
170  arrin = np.copy(np.angle(arr))
171  elif type == 'amplitude':
172  arrin = np.copy(np.abs(arr))
173  else:
174  arrin = np.copy(arr)
175  #arrin = np.float(arrin)
176  arrin -= np.min(arrin)
177  #arrin = arrin + np.abs(arrin)
178  arrin = arrin / (np.max(arrin))
179  return arrin
180 
181  def getRGBImage(self, R, G, B, fname, type='angle'):
182  """Get RGB image"""
183  h, w = R.shape
184  img = np.zeros((h, w, 3))
185  img[:, :, 0] = self.normalize(R, type)
186  img[:, :, 1] = self.normalize(G, type)
187  img[:, :, 2] = self.normalize(B, type)
188  plt.imsave(fname, img)
189  return img
190 
191  def getMonoImage(self, ch, fname):
192  """Get Single channel image"""
193  im = self.normalize(ch, 'angle')
194  phase = fname + '_IM.bmp'
195  plt.imsave(phase, im, cmap='gray')
196  re = self.normalize(ch, 'amplitude')
197  real = fname + '_RE.bmp'
198  plt.imsave(real, re, cmap='gray')
199  return im, re
def h_RS(x1, y1, z1, x2, y2, z2, wvl, pp)
def normalize(self, arr, type='angle')
def Conv(x1, y1, z1, z2, amp, h, w, pp, wvl, method)
def h_Frsn(x1, y1, z1, x2, y2, z2, wvl, pp)
def getRGBImage(self, R, G, B, fname, type='angle')
def __init__(self, plypath, method='RS', propagation_distance=1, angleX=0, angleY=0, Red=639 *nm, Green=525 *nm, Blue=463 *nm, SLM_width=3840, SLM_height=2160, scaleXY=0.03, scaleZ=0.25, pixel_pitch=3.6 *um, multicore=True)