Openholo  v4.2
Open Source Digital Holographic Library
Image2D.py
Go to the documentation of this file.
1 import numpy as np
2 import matplotlib.pyplot as plt
3 from PIL import Image
4 from numba import njit
5 
6 # parameters
7 mm = 1e-3
8 um = mm * mm
9 nm = um * mm
10 
11 
12 @njit
13 def k(wvl):
14  return (np.pi * 2) / wvl
15 
16 
17 @njit
18 def asm_kernel(wvl, z, w, h, pp):
19  deltax = 1 / (w * pp * 2) # sampling period
20  deltay = 1 / ((h + w) * pp)
21  a = np.zeros((w + h, w*2)) # real part
22  b = np.zeros((w + h, w*2)) # imaginary part
23  delx = 1 / (np.sqrt((2 * deltax * z)**2 + 1) * wvl) # bandwidth limitation
24  dely = 1 / (np.sqrt((2 * deltay * z)**2 + 1) * wvl)
25  for i in np.arange(w*2):
26  for j in np.arange(w + h):
27  fx = ((i - w) * deltax)
28  fy = -((j - (w + h)/2) * deltay)
29  if -delx < fx < delx and -dely < fy < dely:
30  a[j, i] = np.cos(2 * np.pi * z * np.sqrt((1/wvl)**2 - fx * fx - fy * fy))
31  b[j, i] = np.sin(2 * np.pi * z * np.sqrt((1/wvl)**2 - fx * fx - fy * fy))
32  print(z, 'kernel ready')
33  return a + 1j * b
34 
35 
36 @njit
37 def h_frsn(pixel_pitch_x, pixel_pitch_y, nx, ny, zz, wvl):
38  re = np.zeros((ny, nx))
39  im = np.zeros((ny, nx))
40  for i in np.arange(nx):
41  for j in np.arange(ny):
42  x = (i - nx / 2) * pixel_pitch_x
43  y = -(j - ny / 2) * pixel_pitch_y
44  re[j, i] = np.cos((np.pi / (wvl * zz)) * (x * x + y * y))
45  im[j, i] = np.sin((np.pi / (wvl * zz)) * (x * x + y * y))
46  return re + 1j * im
47 
48 
49 @njit
50 def refwave(wvl, wr, hr, z, pp, thetaX, thetaY):
51  a = np.zeros((hr, wr))
52  b = np.zeros((hr, wr))
53  for i in np.arange(hr):
54  for j in np.arange(wr):
55  x = (j - wr / 2) * pp
56  y = -(i - hr / 2) * pp
57  a[i, j] = np.cos(k(wvl) * (x * np.sin(thetaX) + y * np.sin(thetaY)))
58  b[i, j] = np.sin(k(wvl) * (x * np.sin(thetaX) + y * np.sin(thetaY)))
59  return (a / z) + 1j * (b / z)
60 
61 
62 class Propagation:
63  """
64  Get Fringe pattern by 2D image
65  Parameters
66  imgpath : image path
67  f : propagation length
68  angle : phase shift angle
69  Red, Green, Blue : wavelength
70  scale : scaling factor
71  http://openholo.org/
72  """
73  def __init__(self, imgpath, propagation_distance=1, angleX=0, angleY=0, Red=639*nm, Green=525*nm, Blue=463*nm,
74  SLM_width=3840, SLM_height=2160, scale=0.03, pixel_pitch=3.6*um):
75  self.zz = propagation_distance
76  self.imagein = np.asarray(Image.open(imgpath))
77  self.thetaX = angleX * (np.pi / 180)
78  self.thetaY = angleY * (np.pi / 180)
79  self.wvl_R = Red
80  self.wvl_G = Green
81  self.wvl_B = Blue
82  self.w = SLM_width
83  self.h = SLM_height
84  self.pp = pixel_pitch
85  self.scale = scale
86 
87  def zeropadding(self, img, nzpw):
88  im = Image.fromarray(img)
89  im = im.resize((self.w, self.h), Image.BILINEAR)
90  im = np.asarray(im)
91  hh = nzpw + self.h
92  ww = nzpw + self.w
93  img_new = np.zeros((hh, ww))
94  img_new[(hh-self.h)//2:(hh+self.h)//2, (ww-self.w)//2:(ww+self.w)//2] = im
95  return img_new
96 
97  def resizeimg(self, wvl, img):
98  """RGB 파장에 맞게 원본 이미지를 리사이징 + zero padding"""
99  w_n = int(self.w * (self.wvl_B / wvl))
100  h_n = int(self.h * (self.wvl_B / wvl))
101  img_new = np.zeros((self.h*2, self.w*2))
102  im = Image.fromarray(img)
103  im = im.resize((w_n, h_n), Image.BILINEAR) # resize image
104  im = np.asarray(im)
105  print(im.shape)
106  img_new[(self.h*2 - h_n) // 2:(self.h*2 + h_n) // 2, (self.w*2 - w_n) // 2:(self.w*2 + w_n) // 2] = im
107  return img_new
108 
109 
110  def Fresnel(self, color):
111  if color == 'green':
112  wvl = self.wvl_G
113  image = np.double(self.resizeimg(self.wvl_G, self.imagein[:, :, 1]))
114  elif color == 'blue':
115  wvl = self.wvl_B
116  image = np.double(self.resizeimg(self.wvl_B, self.imagein[:, :, 2]))
117  else:
118  wvl = self.wvl_R
119  image = np.double(self.resizeimg(self.wvl_R, self.imagein[:, :, 0]))
120  # resize image
121  image = np.flip(image, axis=0)
122  ps = self.scale / (2*self.w)
123  phase = np.random.rand(2*self.h, 2*self.w) * 2 * np.pi # random phase
124  ph = np.exp(1j*phase)
125  ph *= image
126  ch2 = ph * h_frsn(ps, ps, self.w * 2, self.h * 2, self.zz, wvl)
127  CH1 = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(ch2)))
128  result = CH1 * h_frsn(self.pp, self.pp, self.w * 2, self.h * 2, self.zz, wvl)
129  result = result[self.h // 2: (3*self.h) // 2, self.w // 2: (3*self.w) // 2]
130  result *= refwave(wvl, self.w, self.h, self.zz, self.pp, self.thetaX, self.thetaY)
131  return result
132 
133 
134  def ASM(self, color):
135  if color == 'green':
136  wvl = self.wvl_G
137  image = np.double(self.zeropadding(self.imagein[:,:,1], self.w))
138  elif color == 'blue':
139  wvl = self.wvl_B
140  image = np.double(self.zeropadding(self.imagein[:,:,2], self.w))
141  else:
142  wvl = self.wvl_R
143  image = np.double(self.zeropadding(self.imagein[:,:,0], self.w))
144  phase = np.random.rand(self.w + self.h, 2 * self.w) * 2 * np.pi # random phase
145  phase = np.exp(1j * phase)
146  phase *= image
147  CH = self.fft(phase)
148  CH = CH * asm_kernel(wvl, self.zz, self.w, self.h, self.pp)
149  result = self.ifft(CH)
150  result = result[self.w // 2: self.w//2 + self.h, self.w//2: (3 * self.w)//2]
151  result *= refwave(wvl, self.w, self.h, self.zz, self.pp, self.thetaX, self.thetaY)
152  return result
153 
154 
155  # functions
156  def fft(self, f):
157  return np.fft.fftshift(np.fft.fft2(np.fft.fftshift(f)))
158 
159  def ifft(self, f):
160  return np.fft.fftshift(np.fft.ifft2(np.fft.fftshift(f)))
161 
162  def SSB(self, ch):
163  """single side band encoding"""
164  height, width = ch.shape
165  a = np.zeros((height, width), dtype='complex128')
166  CH = self.fft(ch) # fourier transformed image
167  CH = CH[height // 4: (height * 3) // 4, :]
168  a[0:height // 2, :] = CH
169  a[height // 2:, :] = np.conj(CH)
170  return self.ifft(a)
171 
172  def normalize(self, arr, type='angle'):
173  """normalize"""
174  if type == 'phase':
175  arrin = np.copy(np.imag(arr))
176  elif type == 'real':
177  arrin = np.copy(np.real(arr))
178  elif type == 'angle':
179  arrin = np.copy(np.angle(arr))
180  elif type == 'amplitude':
181  arrin = np.copy(np.abs(arr))
182  else:
183  arrin = np.copy(arr)
184  # arrin = np.float(arrin)
185  arrin -= np.min(arrin)
186  # arrin = arrin + np.abs(arrin)
187  arrin = arrin / np.max(arrin)
188  return arrin
189 
190  def getRGBImage(self, R, G, B, fname, type='phase'):
191  """Get RGB image"""
192  h, w = R.shape
193  img = np.zeros((h, w, 3))
194  img[:, :, 0] = self.normalize(R, type)
195  img[:, :, 1] = self.normalize(G, type)
196  img[:, :, 2] = self.normalize(B, type)
197  plt.imsave(fname, img)
198  return img
199 
200  def getMonoImage(self, ch, fname):
201  """Get Single channel image"""
202  im = self.normalize(ch, 'phase')
203  phase = fname + '_IM.bmp'
204  plt.imsave(phase, im, cmap='gray')
205  re = self.normalize(ch, 'real')
206  real = fname + '_RE.bmp'
207  plt.imsave(real, re, cmap='gray')
208  return im, re
def ASM(self, color)
Definition: Image2D.py:134
def getMonoImage(self, ch, fname)
Definition: Image2D.py:200
def zeropadding(self, img, nzpw)
Definition: Image2D.py:87
def h_frsn(pixel_pitch_x, pixel_pitch_y, nx, ny, zz, wvl)
Definition: Image2D.py:37
def resizeimg(self, wvl, img)
Definition: Image2D.py:97
def normalize(self, arr, type='angle')
Definition: Image2D.py:172
def Fresnel(self, color)
Definition: Image2D.py:110
def asm_kernel(wvl, z, w, h, pp)
Definition: Image2D.py:18
def k(wvl)
Definition: Image2D.py:13
def SSB(self, ch)
Definition: Image2D.py:162
def __init__(self, imgpath, propagation_distance=1, angleX=0, angleY=0, Red=639 *nm, Green=525 *nm, Blue=463 *nm, SLM_width=3840, SLM_height=2160, scale=0.03, pixel_pitch=3.6 *um)
Definition: Image2D.py:74
def refwave(wvl, wr, hr, z, pp, thetaX, thetaY)
Definition: Image2D.py:50
def getRGBImage(self, R, G, B, fname, type='phase')
Definition: Image2D.py:190