Openholo  v5.0
Open Source Digital Holographic Library
ophCascadedPropagation.cpp
Go to the documentation of this file.
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install, copy or use the software.
7 //
8 //
9 // License Agreement
10 // For Open Source Digital Holographic Library
11 //
12 // Openholo library is free software;
13 // you can redistribute it and/or modify it under the terms of the BSD 2-Clause license.
14 //
15 // Copyright (C) 2017-2024, Korea Electronics Technology Institute. All rights reserved.
16 // E-mail : contact.openholo@gmail.com
17 // Web : http://www.openholo.org
18 //
19 // Redistribution and use in source and binary forms, with or without modification,
20 // are permitted provided that the following conditions are met:
21 //
22 // 1. Redistribution's of source code must retain the above copyright notice,
23 // this list of conditions and the following disclaimer.
24 //
25 // 2. Redistribution's in binary form must reproduce the above copyright notice,
26 // this list of conditions and the following disclaimer in the documentation
27 // and/or other materials provided with the distribution.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the copyright holder or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 // This software contains opensource software released under GNU Generic Public License,
41 // NVDIA Software License Agreement, or CUDA supplement to Software License Agreement.
42 // Check whether software you use contains licensed software.
43 //
44 //M*/
45 
46 #include "ophCascadedPropagation.h"
47 #include "sys.h"
48 #include "tinyxml2.h"
49 #include <string>
50 #include <cfloat>
51 
52 
53 
54 ophCascadedPropagation::ophCascadedPropagation()
55  : ready_to_propagate(false),
56  hologram_path(L"")
57 {
58 }
59 
60 ophCascadedPropagation::ophCascadedPropagation(const wchar_t* configfilepath)
61  : ready_to_propagate(false),
62  hologram_path(L"")
63 {
64  if (readConfig(configfilepath) && allocateMem())
65  {
66  string hologram_path_str;
67  hologram_path_str.assign(hologram_path.begin(), hologram_path.end());
68  ready_to_propagate = (sourcetype_ == SourceType::IMG && loadInputImg(hologram_path_str)) || (sourcetype_ == SourceType::OHC && loadAsOhc(hologram_path_str.c_str()));
69  }
70 }
71 
73 {
74 }
75 
77 {
78  deallocateMem();
79 }
80 
82 {
83  if (!isReadyToPropagate())
84  {
85  PRINT_ERROR("module not initialized");
86  return false;
87  }
88 
89  if (!propagateSlmToPupil())
90  {
91  PRINT_ERROR("failed to propagate to pupil plane");
92  return false;
93  }
94 
96  {
97  PRINT_ERROR("failed to propagate to retina plane");
98  return false;
99  }
100 
101  return true;
102 }
103 
104 bool ophCascadedPropagation::save(const wchar_t* pathname, uint8_t bitsperpixel)
105 {
106  wstring bufw(pathname);
107  string bufs;
108  bufs.assign(bufw.begin(), bufw.end());
109  oph::uchar* src = getIntensityfields(getRetinaWavefieldAll());
110  return saveAsImg(bufs.c_str(), bitsperpixel, src, getResX(), getResY());
111 }
112 
113 bool ophCascadedPropagation::saveAsOhc(const char * fname)
114 {
115  oph::uint nx = getResX();
116  oph::uint ny = getResY();
117  for (oph::uint i = 0; i < getNumColors(); i++)
118  memcpy(complex_H[i], wavefield_retina[i], nx * ny * sizeof(Complex<Real>));
119 
120  if (!context_.wave_length)
122  for (oph::uint i = 0; i < getNumColors(); i++)
123  context_.wave_length[i] = config_.wavelengths[i];
124  context_.pixel_pitch[0] = config_.dx;
125  context_.pixel_pitch[1] = config_.dy;
126  context_.pixel_number[0] = config_.nx;
127  context_.pixel_number[1] = config_.ny;
128 
129  return Openholo::saveAsOhc(fname);
130 }
131 
132 bool ophCascadedPropagation::loadAsOhc(const char * fname)
133 {
134  if (!Openholo::loadAsOhc(fname))
135  return -1;
136 
137  oph::uint nx = getResX();
138  oph::uint ny = getResY();
139  config_.num_colors = OHC_decoder->getNumOfWavlen();
140  for (oph::uint i = 0; i < getNumColors(); i++)
141  memcpy(wavefield_SLM[i], complex_H[i], nx * ny * sizeof(Complex<Real>));
142 
143  return 1;
144 }
145 
146 bool ophCascadedPropagation::allocateMem()
147 {
148  wavefield_SLM.resize(getNumColors());
149  wavefield_pupil.resize(getNumColors());
150  wavefield_retina.resize(getNumColors());
151  oph::uint nx = getResX();
152  oph::uint ny = getResY();
153  complex_H = new Complex<Real>*[getNumColors()];
154  for (oph::uint i = 0; i < getNumColors(); i++)
155  {
156  wavefield_SLM[i] = new oph::Complex<Real>[nx * ny];
157  wavefield_pupil[i] = new oph::Complex<Real>[nx * ny];
158  wavefield_retina[i] = new oph::Complex<Real>[nx * ny];
159  complex_H[i] = new oph::Complex<Real>[nx * ny];
160  }
161 
162  return true;
163 }
164 
165 void ophCascadedPropagation::deallocateMem()
166 {
167  for (auto e : wavefield_SLM)
168  delete[] e;
169  wavefield_SLM.clear();
170 
171  for (auto e : wavefield_pupil)
172  delete[] e;
173  wavefield_pupil.clear();
174 
175  for (auto e : wavefield_retina)
176  delete[] e;
177  wavefield_retina.clear();
178 
179  for (oph::uint i = 0; i < getNumColors(); i++)
180  delete[] complex_H[i];
181 }
182 
183 // read in hologram data
184 bool ophCascadedPropagation::loadInputImg(string hologram_path_str)
185 {
186  if (!checkExtension(hologram_path_str.c_str(), ".bmp"))
187  {
188  PRINT_ERROR("input file format not supported");
189  return false;
190  }
191  oph::uint nx = getResX();
192  oph::uint ny = getResY();
193  oph::uchar* data = new oph::uchar[nx * ny * getNumColors()];
194  if (!loadAsImgUpSideDown(hologram_path_str.c_str(), data)) // loadAsImg() keeps to fail
195  {
196  PRINT_ERROR("input file not found");
197  delete[] data;
198  return false;
199  }
200 
201  // copy data to wavefield
202  try {
203  oph::uint numColors = getNumColors();
204  for (oph::uint row = 0; row < ny; row++)
205  {
206  for (oph::uint col = 0; col < nx; col++)
207  {
208  for (oph::uint color = 0; color < numColors; color++)
209  {
210  // BGR to RGB & upside-down
211  wavefield_SLM[numColors - 1 - color][(ny - 1 - row) * nx+ col] = oph::Complex<Real>((Real)data[(row * nx + col) * numColors + color], 0);
212  }
213  }
214  }
215  }
216 
217  catch (...) {
218  PRINT_ERROR("failed to generate wavefield from bmp");
219  delete[] data;
220  return false;
221  }
222 
223  delete[] data;
224  return true;
225 }
226 
227 oph::uchar* ophCascadedPropagation::getIntensityfields(vector<oph::Complex<Real>*> waveFields)
228 {
229  oph::uint numColors = getNumColors();
230  if (numColors != 1 && numColors != 3)
231  {
232  PRINT_ERROR("invalid number of color channels");
233  return nullptr;
234  }
235 
236  oph::uint nx = getResX();
237  oph::uint ny = getResY();
238  oph::uchar* intensityField = new oph::uchar[nx * ny * numColors];
239  for (oph::uint color = 0; color < numColors; color++)
240  {
241  Real* intensityFieldUnnormalized = new Real[nx * ny];
242 
243  // find minmax
244  Real maxIntensity = 0.0;
245  Real minIntensity = REAL_IS_DOUBLE ? DBL_MAX : FLT_MAX;
246  for (oph::uint row = 0; row < ny; row++)
247  {
248  for (oph::uint col = 0; col < nx; col++)
249  {
250  intensityFieldUnnormalized[row * nx + col] = waveFields[color][row * nx + col].mag2();
251  maxIntensity = max(maxIntensity, intensityFieldUnnormalized[row * nx + col]);
252  minIntensity = min(minIntensity, intensityFieldUnnormalized[row * nx + col]);
253  }
254  }
255 
256  maxIntensity *= getNor(); // IS IT REALLY NEEDED?
257  if (maxIntensity <= minIntensity)
258  {
259  for (oph::uint row = 0; row < ny; row++)
260  {
261  for (oph::uint col = 0; col < nx; col++)
262  {
263  intensityField[(row * nx + col) * numColors + (numColors - 1 - color)] = 0; // flip RGB order
264  }
265  }
266  }
267  else
268  {
269  for (oph::uint row = 0; row < ny; row++)
270  {
271  for (oph::uint col = 0; col < nx; col++)
272  {
273  Real normalizedVal = (intensityFieldUnnormalized[row * nx + col] - minIntensity) / (maxIntensity - minIntensity);
274  normalizedVal = min(1.0, normalizedVal);
275 
276  // rotate 180 & RGB flip
277  intensityField[((ny - 1 - row) * nx + (nx - 1 - col)) * numColors + (numColors - 1 - color)] = (oph::uchar)(normalizedVal * 255);
278  }
279  }
280  }
281  delete[] intensityFieldUnnormalized;
282  }
283 
284  return intensityField;
285 }
286 
287 bool ophCascadedPropagation::readConfig(const wchar_t* fname)
288 {
289  /*XML parsing*/
290  tinyxml2::XMLDocument xml_doc;
291  tinyxml2::XMLNode *xml_node;
292  wstring fnamew(fname);
293  string fnames;
294  fnames.assign(fnamew.begin(), fnamew.end());
295 
296  if (!checkExtension(fnames.c_str(), ".xml"))
297  {
298  LOG("file's extension is not 'xml'\n");
299  return false;
300  }
301  auto ret = xml_doc.LoadFile(fnames.c_str());
302  if (ret != tinyxml2::XML_SUCCESS)
303  {
304  LOG("Failed to load file \"%s\"\n", fnames.c_str());
305  return false;
306  }
307 
308  xml_node = xml_doc.FirstChild();
309  auto next = xml_node->FirstChildElement("SourceType");
310  if (!next || !(next->GetText()))
311  return false;
312  string sourceTypeStr = (xml_node->FirstChildElement("SourceType"))->GetText();
313  if (sourceTypeStr == string("IMG"))
314  sourcetype_ = SourceType::IMG;
315  else if (sourceTypeStr == string("OHC"))
316  sourcetype_ = SourceType::OHC;
317 
318  next = xml_node->FirstChildElement("NumColors");
319  if (!next || tinyxml2::XML_SUCCESS != next->QueryUnsignedText(&config_.num_colors))
320  return false;
321  if (getNumColors() == 0 || getNumColors() > 3)
322  return false;
323 
324  if (config_.num_colors >= 1)
325  {
326  next = xml_node->FirstChildElement("WavelengthR");
327  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.wavelengths[0]))
328  return false;
329  }
330  if (config_.num_colors >= 2)
331  {
332  next = xml_node->FirstChildElement("WavelengthG");
333  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.wavelengths[1]))
334  return false;
335  }
336  if (config_.num_colors == 3)
337  {
338  next = xml_node->FirstChildElement("WavelengthB");
339  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.wavelengths[2]))
340  return false;
341  }
342 
343  next = xml_node->FirstChildElement("PixelPitchHor");
344  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.dx))
345  return false;
346  next = xml_node->FirstChildElement("PixelPitchVer");
347  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.dy))
348  return false;
349  if (getPixelPitchX() != getPixelPitchY())
350  {
351  PRINT_ERROR("current implementation assumes pixel pitches are same for X and Y axes");
352  return false;
353  }
354 
355  next = xml_node->FirstChildElement("ResolutionHor");
356  if (!next || tinyxml2::XML_SUCCESS != next->QueryUnsignedText(&config_.nx))
357  return false;
358  next = xml_node->FirstChildElement("ResolutionVer");
359  if (!next || tinyxml2::XML_SUCCESS != next->QueryUnsignedText(&config_.ny))
360  return false;
361 
362  if (!context_.wave_length)
364  for (oph::uint i = 0; i < getNumColors(); i++)
365  context_.wave_length[i] = config_.wavelengths[i];
366  context_.pixel_pitch[0] = config_.dx;
367  context_.pixel_pitch[1] = config_.dy;
368  context_.pixel_number[0] = config_.nx;
369  context_.pixel_number[1] = config_.ny;
370 
373  for (oph::uint i = 0; i < getNumColors(); i++)
375 
376  // 2024.04.23. mwnam
377 // set variable for resolution
379 
380  next = xml_node->FirstChildElement("FieldLensFocalLength");
381  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.field_lens_focal_length))
382  return false;
383  next = xml_node->FirstChildElement("DistReconstructionPlaneToPupil");
384  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.dist_reconstruction_plane_to_pupil))
385  return false;
386  next = xml_node->FirstChildElement("DistPupilToRetina");
387  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.dist_pupil_to_retina))
388  return false;
389  next = xml_node->FirstChildElement("PupilDiameter");
390  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.pupil_diameter))
391  return false;
392  next = xml_node->FirstChildElement("Nor");
393  if (!next || tinyxml2::XML_SUCCESS != next->QueryDoubleText(&config_.nor))
394  return false;
395 
396  next = xml_node->FirstChildElement("HologramPath");
397  if (!next || !(next->GetText()))
398  return false;
399  string holopaths = (xml_node->FirstChildElement("HologramPath"))->GetText();
400  hologram_path.assign(holopaths.begin(), holopaths.end());
401 
402  return true;
403 }
404 
406 {
407  auto start_time = CUR_TIME;
408  oph::uint numColors = getNumColors();
409  oph::uint nx = getResX();
410  oph::uint ny = getResY();
411  oph::Complex<Real>* buf = new oph::Complex<Real>[nx * ny];
412  for (oph::uint color = 0; color < numColors; color++)
413  {
414  fft2(getSlmWavefield(color), buf, nx, ny, OPH_FORWARD, false);
415 
416  Real k = 2 * M_PI / getWavelengths()[color];
418  Real dx1 = vw / (Real)nx;
419  Real dy1 = vw / (Real)ny;
420  for (oph::uint row = 0; row < ny; row++)
421  {
422  Real Y1 = ((Real)row - ((Real)ny - 1) * 0.5f) * dy1;
423  for (oph::uint col = 0; col < nx; col++)
424  {
425  Real X1 = ((Real)col - ((Real)nx - 1) * 0.5f) * dx1;
426 
427  // 1st propagation
428  oph::Complex<Real> t1 = oph::Complex<Real>(0, k / 2 / getFieldLensFocalLength() * (X1 * X1 + Y1 * Y1)).exp();
430  buf[row * nx + col] = t1 / t2 * buf[row * nx + col];
431 
432  // applying aperture: need some optimization later
433  if ((sqrt(X1 * X1 + Y1 * Y1) >= getPupilDiameter() / 2) || (row >= ny / 2 - 1))
434  buf[row * nx + col] = 0;
435 
437  oph::Complex<Real> t3 = oph::Complex<Real>(0, -k / 2 / f_eye * (X1 * X1 + Y1 * Y1)).exp();
438  buf[row * nx + col] *= t3;
439  }
440  }
441 
442  memcpy(getPupilWavefield(color), buf, sizeof(oph::Complex<Real>) * nx * ny);
443  }
444 
445  auto end_time = CUR_TIME;
446 
447  auto during_time = ((std::chrono::duration<Real>)(end_time - start_time)).count();
448 
449  LOG("SLM to Pupil propagation - Implement time : %.5lf sec\n", during_time);
450 
451  delete[] buf;
452  return true;
453 }
454 
456 {
457  auto start_time = CUR_TIME;
458  oph::uint numColors = getNumColors();
459  oph::uint nx = getResX();
460  oph::uint ny = getResY();
461  oph::Complex<Real>* buf = new oph::Complex<Real>[nx * ny];
462  for (oph::uint color = 0; color < numColors; color++)
463  {
464  memcpy(buf, getPupilWavefield(color), sizeof(oph::Complex<Real>) * nx * ny);
465 
466  Real k = 2 * M_PI / getWavelengths()[color];
468  Real dx1 = vw / (Real)nx;
469  Real dy1 = vw / (Real)ny;
470  for (oph::uint row = 0; row < ny; row++)
471  {
472  Real Y1 = ((Real)row - ((Real)ny - 1) * 0.5f) * dy1;
473  for (oph::uint col = 0; col < nx; col++)
474  {
475  Real X1 = ((Real)col - ((Real)nx - 1) * 0.5f) * dx1;
476 
477  // 2nd propagation
478  oph::Complex<Real> t1 = oph::Complex<Real>(0, k / 2 / getDistPupilToRetina() * (X1 * X1 + Y1 * Y1)).exp();
479  buf[row * nx + col] *= t1;
480  }
481  }
482 
483  fft2(buf, getRetinaWavefield(color), nx, ny, OPH_FORWARD, false);
484  }
485 
486  auto end_time = CUR_TIME;
487 
488  auto during_time = ((std::chrono::duration<Real>)(end_time - start_time)).count();
489 
490  LOG("Pupil to Retina propagation - Implement time : %.5lf sec\n", during_time);
491 
492  delete[] buf;
493  return true;
494 }
495 
497 {
498  if (wavefield_SLM.size() <= (size_t)id)
499  return nullptr;
500  return wavefield_SLM[id];
501 }
502 
504 {
505  if (wavefield_pupil.size() <= (size_t)id)
506  return nullptr;
507  return wavefield_pupil[id];
508 }
509 
511 {
512  if (wavefield_retina.size() <= (size_t)id)
513  return nullptr;
514  return wavefield_retina[id];
515 }
516 
518 {
519  return wavefield_retina;
520 }
521 
522 
virtual bool saveAsImg(const char *fname, uint8_t bitsperpixel, uchar *src, int width, int height)
Function for creating image files.
Definition: Openholo.cpp:135
oph::uint getResX()
Returns horizontal resolution.
ResolutionConfig resCfg
Definition: Openholo.h:487
bool save(const wchar_t *pathname, uint8_t bitsperpixel)
Save wavefield at retina plane as Windows Bitmap file.
Real getPupilDiameter()
Returns diameter of pupil in meter.
Real getDistPupilToRetina()
Returns distance from pupil plane to retina plane in meter.
void setPixelNumberOHC(const ivec2 pixel_number)
getter/setter for OHC file read and write
Definition: Openholo.h:502
unsigned char uchar
Definition: typedef.h:64
#define CUR_TIME
Definition: function.h:58
float Real
Definition: typedef.h:55
oph::Complex< Real > * getRetinaWavefield(oph::uint id)
Return monochromatic wavefield at retina plane.
Real getFieldLensFocalLength()
Returns focal length of field lens in meter.
bool propagateSlmToPupil()
Calculates 1st propagation (from SLM plane to pupil plane)
void setPixelPitchOHC(const vec2 pixel_pitch)
Definition: Openholo.h:505
virtual bool loadAsOhc(const char *fname)
Function to read OHC file.
bool checkExtension(const char *fname, const char *ext)
Functions for extension checking.
Definition: Openholo.cpp:86
const XMLNode * FirstChild() const
Get the first child node, or null if none exists.
Definition: tinyxml2.h:761
vector< oph::Complex< Real > * > getRetinaWavefieldAll()
Return all wavefields at retina plane.
vec2 pixel_pitch
Definition: Openholo.h:101
virtual bool saveAsOhc(const char *fname)
Function to write OHC file.
def k(wvl)
Definition: Depthmap.py:16
#define M_PI
Definition: define.h:52
#define OPH_FORWARD
Definition: define.h:66
oph::uint getResY()
Returns vertical resolution.
void addWaveLengthOHC(const Real wavelength)
Definition: Openholo.h:534
void fft2(ivec2 n, Complex< Real > *in, int sign=OPH_FORWARD, uint flag=OPH_ESTIMATE)
Functions for performing fftw 2-dimension operations inside Openholo.
Definition: Openholo.cpp:559
#define REAL_IS_DOUBLE
Definition: typedef.h:49
bool propagatePupilToRetina()
Calculates 2nd propagation (from pupil plane to retina plane)
ImgDecoderOhc * OHC_decoder
Definition: Openholo.h:496
oph::vec3 getWavelengths()
Returns wavelengths in meter.
bool isReadyToPropagate()
Returns if all data are prepared.
bool propagate()
Do cascaded propagation.
Complex< T > & exp()
Definition: complex.h:395
Real getPixelPitchY()
Returns vertical pixel pitch in meter.
ivec2 pixel_number
Definition: Openholo.h:99
oph::Complex< Real > * getSlmWavefield(oph::uint id)
Return monochromatic wavefield at SLM plane.
virtual bool loadAsOhc(const char *fname)
Function to read OHC file.
Definition: Openholo.cpp:280
XMLError LoadFile(const char *filename)
Definition: tinyxml2.cpp:2150
virtual void ophFree(void)
Pure virtual function for override in child classes.
virtual bool saveAsOhc(const char *fname)
Function to write OHC file
Definition: Openholo.cpp:252
Complex< Real > ** complex_H
Definition: Openholo.h:490
oph::Complex< Real > * getPupilWavefield(oph::uint id)
Return monochromatic wavefield at pupil plane.
Real getPixelPitchX()
Returns horizontal pixel pitch in meter.
#define PRINT_ERROR(errorMsg)
OphConfig context_
Definition: Openholo.h:486
Real * wave_length
Definition: Openholo.h:106
Real getDistObjectToPupil()
Returns distance from reconstruction plane to pupil plane in meter.
oph::uint getNumColors()
Returns number of colors.
bool loadAsImgUpSideDown(const char *fname, uchar *dst)
Function for loading image files | Output image data upside down.
Definition: Openholo.cpp:353
const XMLElement * FirstChildElement(const char *name=0) const
Definition: tinyxml2.cpp:940
unsigned int uint
Definition: typedef.h:62
Real getNor()
Returns Nor, which affects the range of output intensity.