riCOM_cpp
This repository contains the C++ implementation of the riCOM (Real Time Centre Of Mass) algorithm for 4D Scanning electron microscopy.
Ricom.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu,
2  * University of Antwerp - All Rights Reserved.
3  * You may use, distribute and modify
4  * this code under the terms of the GPL3 license.
5  * You should have received a copy of the GPL3 license with
6  * this file. If not, please visit:
7  * https://www.gnu.org/licenses/gpl-3.0.en.html
8  *
9  * Authors:
10  * Thomas Friedrich <thomas.friedrich@uantwerpen.be>
11  * Chu-Ping Yu <chu-ping.yu@uantwerpen.be>
12  */
13 
14 #include "Ricom.h"
15 
17 // RICOM Kernel class method implementations //
19 
20 // Compute the kernel
22 {
23  float rot_rad = M_PI * rotation / 180;
24  float cos_rot = cos(rot_rad);
25  float sin_rot = sin(rot_rad);
26 
27  k_width_sym = kernel_size * 2 + 1;
29  kernel_x.assign(k_area, 0);
30  kernel_y.assign(k_area, 0);
31 
32  for (int iy = 0; iy < k_width_sym; iy++)
33  {
34  int iy_e = (iy + 1) * k_width_sym - 1;
35  for (int ix = 0; ix < k_width_sym; ix++)
36  {
37  int ix_s = ix - kernel_size;
38  int iy_s = iy - kernel_size;
39  float d = ix_s * ix_s + iy_s * iy_s;
40  int ix_e = k_area - iy_e + ix - 1;
41 
42  if (d > 0)
43  {
44  float ix_sd = (ix_s / d);
45  float iy_sd = (iy_s / d);
46  kernel_x[ix_e] = cos_rot * ix_sd - sin_rot * iy_sd;
47  kernel_y[ix_e] = sin_rot * ix_sd + cos_rot * iy_sd;
48  }
49  else
50  {
51  kernel_y[ix_e] = 0;
52  kernel_x[ix_e] = 0;
53  }
54  }
55  }
56 
57  // Add filter
58  if (b_filter)
59  {
62  }
63 }
64 
65 // Compute the filter
67 {
68  kernel_filter.assign(k_area, 0);
69  float lb = pow(kernel_filter_frequency[0], 2);
70  float ub = pow(kernel_filter_frequency[1], 2);
71 
72  for (int iy = 0; iy < k_width_sym; iy++)
73  {
74  for (int ix = 0; ix < k_width_sym; ix++)
75  {
76  float dist = pow(ix - kernel_size, 2) + pow(iy - kernel_size, 2);
77  int ic = iy * k_width_sym + ix;
78  if (dist <= ub && dist > lb)
79  {
80  kernel_filter[ic] = 1;
81  }
82  }
83  }
84 }
85 
86 // Applies the filter to the kernel
88 {
89  FFT2D fft2d(k_width_sym, k_width_sym);
90  std::vector<std::complex<float>> x2c = FFT2D::r2c(kernel_x);
91  std::vector<std::complex<float>> y2c = FFT2D::r2c(kernel_y);
92  fft2d.fft(x2c, x2c);
93  fft2d.fft(y2c, y2c);
94  for (int id = 0; id < k_area; id++)
95  {
96  if (kernel_filter[id] == 0.0f)
97  {
98  x2c[id] = {0, 0};
99  y2c[id] = {0, 0};
100  }
101  }
102  fft2d.ifft(x2c, x2c);
103  fft2d.ifft(y2c, y2c);
104  for (int id = 0; id < k_area; id++)
105  {
106  kernel_x[id] = x2c[id].real();
107  kernel_y[id] = y2c[id].real();
108  }
109 }
110 
112 {
113  f_approx.resize(nx_im);
114  float f_max = 0;
115  float k = kernel_size * 2;
116  for (size_t i = 0; i < nx_im; i++)
117  {
118  float x = 2 * i * M_PI;
119  f_approx[i] = (nx_im / x) * (1 - cos(k / 2 * (x / nx_im)));
120  if (f_approx[i] > f_max)
121  {
122  f_max = f_approx[i];
123  }
124  }
125  std::for_each(f_approx.begin(), f_approx.end(), [f_max](float &x) { x/=f_max; });
126 }
127 
129 {
130  // Creating SDL Surface (holding the ricom image in CPU memory)
131  srf_kx = SDL_CreateRGBSurface(0, k_width_sym, k_width_sym, 32, 0, 0, 0, 0);
132  if (srf_kx == NULL)
133  {
134  std::cout << "Surface could not be created! SDL Error: " << SDL_GetError() << std::endl;
135  }
136  srf_ky = SDL_CreateRGBSurface(0, k_width_sym, k_width_sym, 32, 0, 0, 0, 0);
137  if (srf_ky == NULL)
138  {
139  std::cout << "Surface could not be created! SDL Error: " << SDL_GetError() << std::endl;
140  }
141  // determine location index of value in memory
142  std::pair kx_min_max = std::minmax_element(kernel_x.begin(), kernel_x.end());
143  std::pair ky_min_max = std::minmax_element(kernel_y.begin(), kernel_y.end());
144  // Update pixel at location
145 
146  for (int y = 0; y < k_width_sym; y++)
147  {
148  int ic = y * k_width_sym;
149  for (int x = 0; x < k_width_sym; x++)
150  {
151  float valx = (kernel_x[ic + x] - kx_min_max.first[0]) / (kx_min_max.second[0] - kx_min_max.first[0]);
152  SDL_Utils::draw_pixel(srf_kx, x, y, valx, 5);
153  float valy = (kernel_y[ic + x] - ky_min_max.first[0]) / (ky_min_max.second[0] - ky_min_max.first[0]);
154  SDL_Utils::draw_pixel(srf_ky, x, y, valy, 5);
155  }
156  }
157 }
158 // Computer detector distance map relative to a given centre (offset) for vSTEM
159 void Ricom_detector::compute_detector(int nx_cam, int ny_cam, std::array<float, 2> &offset)
160 {
161 
162  radius2[0] = pow(radius[0], 2);
163  radius2[1] = pow(radius[1], 2);
164  float d2;
165  id_list.clear();
166  id_list.reserve(nx_cam * ny_cam);
167 
168  for (int iy = 0; iy < ny_cam; iy++)
169  {
170  for (int ix = 0; ix < nx_cam; ix++)
171  {
172  d2 = pow((float)ix - offset[0], 2) + pow((float)iy - offset[1], 2);
173  if (d2 > radius2[0] && d2 <= radius2[1])
174  {
175  id_list.push_back(iy * nx_cam + ix);
176  }
177  }
178  }
179 }
180 
182 // Counter x-y position class methods //
184 id_x_y::id_x_y(int id, bool valid)
185 {
186  this->id = id;
187  this->valid = valid;
188 };
189 
190 void Update_list::init(Ricom_kernel kernel, int nx_ricom, int ny_ricom)
191 {
192  this->nx = nx_ricom;
193  this->ny = ny_ricom;
194  this->kernel = kernel;
195 
196  ids.resize(kernel.k_area);
197  int idul = 0;
198  for (int idy = -kernel.kernel_size; idy <= kernel.kernel_size; idy++)
199  {
200  for (int idx = -kernel.kernel_size; idx <= kernel.kernel_size; idx++)
201  {
202  ids[idul] = {(idy * nx + idx), false};
203  idul++;
204  }
205  }
206 }
207 
208 void Update_list::shift(id_x_y &res, int id, int shift)
209 {
210  int id_sft = ids[id].id + shift;
211  int y = id_sft / nx;
212  int x = id_sft % nx;
213  res = {id_sft, y >= 0 && y < ny && x < nx && x >= 0};
214 }
215 
217 // SDL plotting //
219 void Ricom::init_surface()
220 {
221  // Creating SDL Surface (holding the ricom image in CPU memory)
222  srf_ricom = SDL_CreateRGBSurface(0, nx, ny, 32, 0, 0, 0, 0);
223  if (srf_ricom == NULL)
224  {
225  std::cout << "Surface could not be created! SDL Error: " << SDL_GetError() << std::endl;
226  exit(EXIT_FAILURE);
227  }
228  // SDL surface for STEM image
229  srf_stem = SDL_CreateRGBSurface(0, nx, ny, 32, 0, 0, 0, 0);
230  if (srf_stem == NULL)
231  {
232  std::cout << "Surface could not be created! SDL Error: " << SDL_GetError() << std::endl;
233  exit(EXIT_FAILURE);
234  }
235  // SDL surface for STEM image
236  srf_e_mag = SDL_CreateRGBSurface(0, nx, ny, 32, 0, 0, 0, 0);
237  if (srf_e_mag == NULL)
238  {
239  std::cout << "Surface could not be created! SDL Error: " << SDL_GetError() << std::endl;
240  exit(EXIT_FAILURE);
241  }
242  // SDL surface for CBED image
243  cbed_log.assign(camera.nx_cam * camera.ny_cam, 0.0);
244  srf_cbed = SDL_CreateRGBSurface(0, camera.nx_cam, camera.ny_cam, 32, 0, 0, 0, 0);
245  if (srf_cbed == NULL)
246  {
247  std::cout << "Surface could not be created! SDL Error: " << SDL_GetError() << std::endl;
248  exit(EXIT_FAILURE);
249  }
250 }
251 
253 // RICOM class method implementations //
255 Ricom::Ricom() : stem_max(-FLT_MAX), stem_min(FLT_MAX),
256  update_list(),
257  e_mag_max(-FLT_MAX), e_mag_min(FLT_MAX),
258  ricom_max(-FLT_MAX), ricom_min(FLT_MAX),
259  cbed_log(),
260  ricom_mutex(), stem_mutex(), counter_mutex(), e_field_mutex(),
261  socket(), file_path(""),
262  camera(),
263  mode(RICOM::FILE),
264  b_print2file(false),
265  redraw_interval(50),
266  last_y(0),
267  p_prog_mon(nullptr),
268  b_busy(false),
269  update_offset(true),
270  b_vSTEM(false), b_e_mag(false), b_plot_cbed(true), b_plot2SDL(false),
271  b_recompute_detector(false), b_recompute_kernel(false),
272  detector(),
273  kernel(),
274  offset{127.5, 127.5}, com_public{0.0, 0.0},
275  com_map_x(), com_map_y(),
276  ricom_data(),
277  stem_data(),
278  e_field_data(),
279  nx(256), ny(256), nxy(0),
280  rep(1), fr_total(0),
281  skip_row(1), skip_img(0),
282  n_threads(1), queue_size(64),
283  fr_freq(0.0), fr_count(0.0), fr_count_total(0.0),
284  rescale_ricom(false), rescale_stem(false),
285  rc_quit(false),
286  srf_ricom(NULL), ricom_cmap(9),
287  srf_stem(NULL), stem_cmap(9),
288  srf_cbed(NULL), cbed_cmap(5),
289  srf_e_mag(NULL), e_mag_cmap(12)
290 {
291  n_threads_max = std::thread::hardware_concurrency();
292 }
293 
295 
296 // Change the endianness of the incoming data
297 template <typename T>
298 void Ricom::swap_endianess(T &val)
299 {
300  if (val > 0 && camera.swap_endian)
301  {
302  switch (sizeof(T))
303  {
304  case 2:
305  val = (val >> 8) | ((val & 0xff) << 8);
306  break;
307  case 4:
308  val = (val >> 24) | ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8);
309  default:
310  break;
311  }
312  }
313 }
314 
315 // Compute the centre of mass
316 template <typename T>
317 void Ricom::com(std::vector<T> *data, std::array<float, 2> &com)
318 {
319  float dose = 0;
320  std::vector<size_t> sum_x(camera.nx_cam);
321  std::vector<size_t> sum_y(camera.ny_cam);
322  sum_x.assign(camera.nx_cam, 0);
323  sum_y.assign(camera.ny_cam, 0);
324  com = {0.0, 0.0};
325 
326  for (int idy = 0; idy < camera.ny_cam; idy++)
327  {
328  size_t y_nx = idy * camera.nx_cam;
329  size_t sum_x_temp = 0;
330  for (int idx = 0; idx < camera.nx_cam; idx++)
331  {
332  T px = data->data()[y_nx + idx];
333  swap_endianess(px);
334  sum_x_temp += px;
335  sum_y[idx] += px;
336  }
337  sum_x[idy] = sum_x_temp;
338  dose += sum_x_temp;
339  }
340 
341  if (dose > 0)
342  {
343  for (int i = 0; i < camera.nx_cam; i++)
344  {
345  com[0] += sum_x[i] * camera.v[i];
346  }
347  for (int i = 0; i < camera.ny_cam; i++)
348  {
349  com[1] += sum_y[i] * camera.u[i];
350  }
351  for (int i = 0; i < 2; i++)
352  {
353  com[i] = (com[i] / dose);
354  }
355  }
356 }
357 
358 // Compute STEM signal
359 template <typename T>
360 void Ricom::stem(std::vector<T> *data, size_t id_stem)
361 {
362  T px;
363  size_t stem_temp = 0;
364  for (size_t id : detector.id_list)
365  {
366  px = (*data)[id];
367  if (px > 0)
368  {
369  swap_endianess(px);
370  stem_temp += px;
371  if (stem_temp > stem_max)
372  {
373  stem_max = stem_temp;
374  rescale_stem = true;
375  }
376  if (stem_temp < stem_min)
377  {
378  stem_min = stem_temp;
379  rescale_stem = true;
380  }
381  }
382  }
383  stem_data[id_stem] = stem_temp;
384 }
385 
386 // Integrate COM around position x,y
387 void Ricom::icom(std::array<float, 2> *com, int x, int y)
388 {
389  float com_x = (*com)[0] - offset[0];
390  float com_y = (*com)[1] - offset[1];
391  id_x_y idr;
392  int idc = x + y * nx;
393 
394  for (int id = 0; id < kernel.k_area; id++)
395  {
396  update_list.shift(idr, id, idc);
397  if (idr.valid)
398  {
399  ricom_data[idr.id] += com_x * kernel.kernel_x[id] + com_y * kernel.kernel_y[id];
400  }
401  }
402  if (ricom_data[idc] > ricom_max)
403  {
404  ricom_max = ricom_data[idc];
405  rescale_ricom = true;
406  }
407  if (ricom_data[idc] < ricom_min)
408  {
409  ricom_min = ricom_data[idc];
410  rescale_ricom = true;
411  }
412 }
413 
414 // Integrate COM around position x,y
415 void Ricom::icom(std::array<float, 2> com, int x, int y)
416 {
417  float com_x = com[0] - offset[0];
418  float com_y = com[1] - offset[1];
419  id_x_y idr;
420  int idc = x + y * nx;
421 
422  for (int id = 0; id < kernel.k_area; id++)
423  {
424  update_list.shift(idr, id, idc);
425  if (idr.valid)
426  {
427  ricom_data[idr.id] += com_x * kernel.kernel_x[id] + com_y * kernel.kernel_y[id];
428  }
429  }
430  if (ricom_data[idc] > ricom_max)
431  {
432  ricom_max = ricom_data[idc];
433  rescale_ricom = true;
434  }
435  if (ricom_data[idc] < ricom_min)
436  {
437  ricom_min = ricom_data[idc];
438  rescale_ricom = true;
439  }
440 }
441 
442 // Redraws the entire ricom image
444 {
445  std::lock_guard<std::mutex> lock(ricom_mutex);
446  for (int y = 0; y < ny; y++)
447  {
448  for (int x = 0; x < nx; x++)
449  {
450  set_ricom_pixel(x, y);
451  }
452  }
453 }
454 
455 // Redraws the ricom image from line y0 to line ye
456 void Ricom::draw_ricom_image(int y0, int ye)
457 {
458  std::lock_guard<std::mutex> lock(ricom_mutex);
459  for (int y = y0; y <= ye; y++)
460  {
461  for (int x = 0; x < nx; x++)
462  {
463  set_ricom_pixel(x, y);
464  }
465  }
466 }
467 
468 // set pixel in ricom image srf_ricom at location idx, idy to value corresponding
469 // value in ricom_data array
470 void Ricom::set_ricom_pixel(int idx, int idy)
471 {
472  // determine location index of value in memory
473  int idr = idy * nx + idx;
474  float val = (ricom_data[idr] - ricom_min) / (ricom_max - ricom_min);
475 
476  // Update pixel at location
477  SDL_Utils::draw_pixel(srf_ricom, idx, idy, val, ricom_cmap);
478 }
479 
480 // Redraws the entire stem image
482 {
483  std::lock_guard<std::mutex> lock(stem_mutex);
484  for (int y = 0; y < ny; y++)
485  {
486  for (int x = 0; x < nx; x++)
487  {
488  set_stem_pixel(x, y);
489  }
490  }
491 }
492 
493 // Redraws the stem image from line y0 to line ye
494 void Ricom::draw_stem_image(int y0, int ye)
495 {
496  std::lock_guard<std::mutex> lock(stem_mutex);
497  for (int y = y0; y <= ye; y++)
498  {
499  for (int x = 0; x < nx; x++)
500  {
501  set_stem_pixel(x, y);
502  }
503  }
504 }
505 
506 void Ricom::draw_e_field_image(int y0, int ye)
507 {
508  std::lock_guard<std::mutex> lock(e_field_mutex);
509  for (int y = y0; y <= ye; y++)
510  {
511  for (int x = 0; x < nx; x++)
512  {
513  set_e_field_pixel(x, y);
514  }
515  }
516 }
517 
519 {
520  std::lock_guard<std::mutex> lock(e_field_mutex);
521  for (int y = 0; y < ny; y++)
522  {
523  for (int x = 0; x < nx; x++)
524  {
525  set_e_field_pixel(x, y);
526  }
527  }
528 }
529 
530 // set pixel in stem image srf_stem at location idx, idy to value corresponding
531 // value in stem_data array
532 void Ricom::set_stem_pixel(size_t idx, size_t idy)
533 {
534  // determine location index of value in memory
535  int idr = idy * nx + idx;
536  float val = (stem_data[idr] - stem_min) / (stem_max - stem_min);
537 
538  // Update pixel at location
539  SDL_Utils::draw_pixel(srf_stem, idx, idy, val, stem_cmap);
540 }
541 
542 // set pixel in stem image srf_stem at location idx, idy to value corresponding
543 // value in stem_data array
544 void Ricom::set_e_field_pixel(size_t idx, size_t idy)
545 {
546  // determine location index of value in memory
547  int idr = idy * nx + idx;
548  float mag = (abs(e_field_data[idr]) - e_mag_min) / (e_mag_max - e_mag_min);
549  float ang = arg(e_field_data[idr]);
550 
551  // Update pixel at location
552  SDL_Utils::draw_pixel(srf_e_mag, idx, idy, ang, mag, e_mag_cmap);
553 }
554 
555 // Draw a CBED in log-scale to the SDL surface srf_cbed
556 template <typename T>
557 void Ricom::plot_cbed(std::vector<T> *cbed_data)
558 {
559  float v_min = INFINITY;
560  float v_max = 0.0;
561 
562  for (size_t id = 0; id < (*cbed_data).size(); id++)
563  {
564  T vl = (*cbed_data)[id];
565  swap_endianess(vl);
566  float vl_f = log1p((float)vl);
567  if (vl_f > v_max)
568  {
569  v_max = vl_f;
570  }
571  if (vl_f < v_min)
572  {
573  v_min = vl_f;
574  }
575  cbed_log[id] = vl_f;
576  }
577 
578  float v_rng = v_max - v_min;
579  for (int ix = 0; ix < camera.ny_cam; ix++)
580  {
581  int iy_t = camera.v[ix] * camera.nx_cam;
582  for (int iy = 0; iy < camera.nx_cam; iy++)
583  {
584  float vl_f = cbed_log[iy_t + camera.u[iy]];
585  float val = (vl_f - v_min) / v_rng;
587  }
588  }
589 }
590 
591 // Rescales the images according to updated min and max values
592 // and recomputes the Kernel if settings changed
593 inline void Ricom::rescales_recomputes()
594 {
596  {
598  b_recompute_detector = false;
599  }
600  if (b_recompute_kernel)
601  {
603  update_list.init(kernel, nx, ny);
604  b_recompute_kernel = false;
605  }
606 
607  if (rescale_ricom)
608  {
609  rescale_ricom = false;
610  };
611  if (b_vSTEM)
612  {
613  if (rescale_stem)
614  {
615  rescale_stem = false;
616  };
617  };
618 
619  if (b_e_mag)
620  {
621  if (rescale_e_mag)
622  {
623  rescale_e_mag = false;
624  };
625  };
626 }
627 
628 // Skip n frames
629 template <typename T, class CameraInterface>
630 void Ricom::skip_frames(int n_skip, std::vector<T> &data, CAMERA::Camera<CameraInterface, CAMERA::FRAME_BASED> *camera_fr)
631 {
632  for (int si = 0; si < n_skip; si++)
633  {
634  camera_fr->read_frame(data, true);
635  }
636 }
637 
638 // Compute COM and iCOM for a frame (copy data)
639 template <typename T>
640 void Ricom::com_icom(std::vector<T> data, int ix, int iy, std::array<float, 2> *com_xy_sum, ProgressMonitor *p_prog_mon)
641 {
642  std::vector<T> *data_ptr = &data;
643 
644  std::array<float, 2> com_xy = {0.0, 0.0};
645  com<T>(data_ptr, com_xy);
646  icom(com_xy, ix, iy);
647 
648  size_t id = iy * nx + ix;
649 
650  if (b_vSTEM)
651  {
652  stem(data_ptr, id);
653  }
654  if (b_e_mag)
655  {
656  compute_electric_field(com_xy, id);
657  }
658 
659  com_xy_sum->at(0) += com_xy[0];
660  com_xy_sum->at(1) += com_xy[1];
661 
662  com_map_x[id] = com_xy[0];
663  com_map_y[id] = com_xy[1];
664 
665  counter_mutex.lock();
666  ++(*p_prog_mon);
668  if (p_prog_mon->report_set)
669  {
670  update_surfaces(iy, data_ptr);
671  last_y = iy;
673  rescales_recomputes();
674  for (int i = 0; i < 2; i++)
675  {
676  com_public[i] = com_xy_sum->at(i) / p_prog_mon->fr_count_i;
677  com_xy_sum->at(i) = 0;
678  }
680  }
681  counter_mutex.unlock();
682 }
683 
684 // Compute COM and iCOM for a frame (reference data)
685 template <typename T>
686 void Ricom::com_icom(std::vector<T> *data_ptr, int ix, int iy, std::array<float, 2> *com_xy_sum, ProgressMonitor *p_prog_mon)
687 {
688  std::array<float, 2> com_xy = {0.0, 0.0};
689  com<T>(data_ptr, com_xy);
690  icom(com_xy, ix, iy);
691 
692  size_t id = iy * nx + ix;
693  if (b_vSTEM)
694  {
695  stem(data_ptr, id);
696  }
697  if (b_e_mag)
698  {
699  compute_electric_field(com_xy, id);
700  }
701  com_xy_sum->at(0) += com_xy[0];
702  com_xy_sum->at(1) += com_xy[1];
703 
704  ++(*p_prog_mon);
705  com_map_x[id] = com_xy[0];
706  com_map_y[id] = com_xy[1];
708  if (p_prog_mon->report_set)
709  {
710  update_surfaces(iy, data_ptr);
711  last_y = iy;
713  rescales_recomputes();
714  for (int i = 0; i < 2; i++)
715  {
716  com_public[i] = com_xy_sum->at(i) / p_prog_mon->fr_count_i;
717  com_xy_sum->at(i) = 0;
718  }
720  }
721 }
722 
723 // Compute electric field magnitude
724 void Ricom::compute_electric_field(std::array<float, 2> &com_xy, size_t id)
725 {
726  float e_mag = std::hypot(com_xy[0] - offset[0], com_xy[1] - offset[1]);
727  float e_ang = atan2(com_xy[0] - offset[0], com_xy[1] - offset[1]);
728  if (e_mag > e_mag_max)
729  {
730  e_mag_max = e_mag;
731  rescale_e_mag = true;
732  }
733  if (e_mag < e_mag_min)
734  {
735  e_mag_min = e_mag;
736  rescale_e_mag = true;
737  }
738  e_field_data[id] = std::polar(e_mag, e_ang);
739 }
740 
741 // Process FRAME_BASED camera data
742 template <typename T, class CameraInterface>
744 {
745  // Memory allocation
746  int cam_xy = camera_spec->nx_cam * camera_spec->ny_cam;
747  std::vector<T> data(cam_xy);
748  std::vector<T> *p_data = &data;
749  BoundedThreadPool pool;
750 
751  // Start Thread Pool
752  if (n_threads > 1)
753  pool.init(n_threads, queue_size);
754 
755  std::array<float, 2> com_xy_sum = {0.0, 0.0};
756  std::array<float, 2> *p_com_xy_sum = &com_xy_sum;
757 
758  // Initialize ProgressMonitor Object
760  p_prog_mon = &prog_mon;
761 
762  for (int ir = 0; ir < rep; ir++)
763  {
764  reinit_vectors_limits();
765  for (int iy = 0; iy < ny; iy++)
766  {
767  for (int ix = 0; ix < nx; ix++)
768  {
769  camera_spec->read_frame(data, !p_prog_mon->first_frame);
770  p_prog_mon->first_frame = false;
771  if (n_threads > 1)
772  {
773  pool.push_task([=]
774  { com_icom<T>(data, ix, iy, p_com_xy_sum, p_prog_mon); });
775  }
776  else
777  {
778  com_icom<T>(p_data, ix, iy, p_com_xy_sum, p_prog_mon);
779  }
780 
781  if (rc_quit)
782  {
783  pool.wait_for_completion();
784  p_prog_mon = nullptr;
785  return;
786  };
787  }
788  skip_frames(skip_row, data, camera_spec);
789  }
790  skip_frames(skip_img, data, camera_spec);
791 
792  if (n_threads > 1)
793  pool.wait_for_completion();
794 
795  if (update_offset)
796  {
797  offset[0] = com_public[0];
798  offset[1] = com_public[1];
799  }
800  }
801  p_prog_mon = nullptr;
802 }
803 
804 template <typename T>
805 void Ricom::update_surfaces(int iy, std::vector<T> *p_frame)
806 {
807  if (b_plot2SDL)
808  {
809  draw_ricom_image((std::max)(0, last_y - kernel.kernel_size), (std::min)(iy + kernel.kernel_size, ny - 1));
810  if (b_vSTEM)
811  {
812  draw_stem_image(last_y, iy);
813  }
814  if (b_e_mag)
815  {
817  }
818  }
819  if (b_plot_cbed)
820  {
821  plot_cbed(p_frame);
822  }
823 }
824 // Process EVENT_BASED camera data
825 template <class CameraInterface>
827 {
828  // Memory allocation
829  std::vector<size_t> dose_map(nxy);
830  std::vector<size_t> sumx_map(nxy);
831  std::vector<size_t> sumy_map(nxy);
832  std::vector<uint16_t> frame(camera_spec->nx_cam * camera_spec->ny_cam);
833  std::vector<uint16_t> *p_frame = &frame;
834 
835  BoundedThreadPool pool;
836 
837  // Start Thread Pool
838  if (n_threads > 1)
839  pool.init(n_threads, queue_size);
840 
841  std::array<float, 2> com_xy = {0.0, 0.0};
842  std::array<float, 2> *p_com_xy = &com_xy;
843  std::array<float, 2> com_xy_sum = {0.0, 0.0};
844 
845  int ix = 0;
846  int iy = 0;
847  int acc_cbed = 0;
848  int acc_idx = 0;
849 
850  size_t img_num = 0;
851  size_t first_frame = img_num * nxy;
852  size_t end_frame = (img_num + 1) * nxy;
853  size_t fr_total_u = (size_t)fr_total;
854 
856  p_prog_mon = &prog_mon;
857 
858  dose_map.assign(nxy, 0);
859  sumx_map.assign(nxy, 0);
860  sumy_map.assign(nxy, 0);
861  reinit_vectors_limits();
862 
863  while (true)
864  {
865  // process two frames before the probe position to avoid toa problem
866  int idxx = prog_mon.fr_count - nxy * img_num - 2;
867 
868  ++prog_mon;
869  fr_count = prog_mon.fr_count;
870 
871  if (acc_cbed < 3 && b_plot_cbed)
872  {
873  camera_spec->read_frame_com_cbed(prog_mon.fr_count,
874  dose_map, sumx_map, sumy_map,
877  frame, acc_idx,
878  first_frame, end_frame);
879  acc_cbed += 1;
880  }
881  else
882  {
883  camera_spec->read_frame_com(prog_mon.fr_count,
884  dose_map, sumx_map, sumy_map,
887  first_frame, end_frame);
888  }
889 
890  if (idxx >= 0)
891  {
892  if (dose_map[idxx] == 0)
893  {
894  com_xy[0] = offset[0];
895  com_xy[1] = offset[1];
896  }
897  else
898  {
899  com_xy[0] = sumx_map[idxx] / dose_map[idxx];
900  com_xy[1] = sumy_map[idxx] / dose_map[idxx];
901  }
902  com_map_x[idxx] = com_xy[0];
903  com_map_y[idxx] = com_xy[1];
904  com_xy_sum[0] += com_xy[0];
905  com_xy_sum[1] += com_xy[1];
906 
907  ix = idxx % nx;
908  iy = floor(idxx / nx);
909 
910  if (n_threads > 1)
911  {
912  pool.push_task([=]
913  { icom(com_xy, ix, iy); });
914  }
915  else
916  {
917  icom(p_com_xy, ix, iy);
918  }
919  if (b_e_mag)
920  {
921  compute_electric_field(com_xy, idxx);
922  }
923  }
924 
925  if (prog_mon.report_set)
926  {
927  update_surfaces(iy, p_frame);
928  if (b_plot_cbed)
929  {
930  frame.assign(camera_spec->nx_cam * camera_spec->ny_cam, 0);
931  acc_cbed = 0;
932  acc_idx = fr_count;
933  }
934  fr_freq = prog_mon.fr_freq;
935  rescales_recomputes();
936  for (int i = 0; i < 2; i++)
937  {
938  com_public[i] = com_xy_sum[i] / prog_mon.fr_count_i;
939  com_xy_sum[i] = 0;
940  }
941  prog_mon.reset_flags();
942  last_y = iy;
943  }
944 
945  if (prog_mon.fr_count >= end_frame)
946  {
947  if (prog_mon.fr_count != fr_total_u)
948  {
949  img_num++;
950  first_frame = img_num * nxy;
951  end_frame = (img_num + 1) * nxy;
952  reinit_vectors_limits();
953  dose_map.assign(nxy, 0);
954  sumx_map.assign(nxy, 0);
955  sumy_map.assign(nxy, 0);
956  }
957 
958  if (update_offset)
959  {
960  offset[0] = com_public[0];
961  offset[1] = com_public[1];
962  }
963  }
964 
965  if (prog_mon.fr_count == fr_total_u || rc_quit)
966  {
967  pool.wait_for_completion();
968  p_prog_mon = nullptr;
969  return;
970  }
971  }
972  p_prog_mon = nullptr;
973 }
974 
975 // Entrance function for Ricom_reconstructinon
976 template <class CameraInterface>
978 {
979  reset();
980  this->mode = mode;
981  b_busy = true;
982  // Initializations
983  nxy = nx * ny;
984  fr_total = nxy * rep;
985  fr_count = 0;
986 
988  init_surface();
989 
990  if (b_vSTEM)
991  {
993  }
994 
995  // Compute the integration Kenel
997 
998  // Allocate the ricom image and COM arrays
999  ricom_data.resize(nxy);
1000  update_list.init(kernel, nx, ny);
1001  com_map_x.resize(nxy);
1002  com_map_y.resize(nxy);
1003  stem_data.resize(nxy);
1004  e_field_data.resize(nxy);
1005 
1006  // Run camera dependent pipeline
1007  // Implementations are in the Interface Wrappers (src/cameras/XXXWrapper.cpp), but essentially
1008  // they run Ricom::process_data<FRAME_BASED>() or Ricom::process_data<EVENT_BASED>()
1009  switch (camera.type)
1010  {
1011  case CAMERA::FRAME_BASED:
1012  {
1014  camera_interface.run(this);
1015  break;
1016  }
1017  case CAMERA::EVENT_BASED:
1018  {
1020  camera_interface.run(this);
1021  break;
1022  }
1023  }
1024  b_busy = false;
1025  std::cout << std::endl
1026  << "Reconstruction finished successfully." << std::endl;
1027 }
1028 
1029 // Template specializations, necessary to avoid linker error
1030 template void Ricom::run_reconstruction<MerlinInterface>(RICOM::modes);
1031 template void Ricom::run_reconstruction<TimepixInterface>(RICOM::modes);
1032 template void Ricom::process_data<uint8_t>(CAMERA::Camera<MerlinInterface, CAMERA::FRAME_BASED> *camera_spec);
1033 template void Ricom::process_data<uint16_t>(CAMERA::Camera<MerlinInterface, CAMERA::FRAME_BASED> *camera_spec);
1035 
1036 // Helper functions
1037 void Ricom::reset_limits()
1038 {
1039  ricom_max = -FLT_MAX;
1040  ricom_min = FLT_MAX;
1041  stem_max = -FLT_MAX;
1042  stem_min = FLT_MAX;
1043 }
1044 
1045 void Ricom::reinit_vectors_limits()
1046 {
1047  ricom_data.assign(nxy, 0);
1048  stem_data.assign(nxy, 0);
1049  com_map_x.assign(nxy, 0);
1050  com_map_y.assign(nxy, 0);
1051  last_y = 0;
1052  reset_limits();
1053 }
1054 
1056 {
1057  rc_quit = false;
1058  fr_freq = 0;
1059  reinit_vectors_limits();
1060 }
1061 
1062 enum CAMERA::Camera_model Ricom::select_mode_by_file(const char *filename)
1063 {
1064  file_path = filename;
1065  if (std::filesystem::path(filename).extension() == ".t3p")
1066  {
1067  mode = RICOM::FILE;
1068  return CAMERA::TIMEPIX;
1069  }
1070  else if (std::filesystem::path(filename).extension() == ".mib")
1071  {
1072  mode = RICOM::FILE;
1073  return CAMERA::MERLIN;
1074  }
1075  else
1076  {
1077  return CAMERA::TIMEPIX;
1078  }
1079 }
1080 
1082 {
1083  switch (r->camera.model)
1084  {
1085  case CAMERA::MERLIN:
1086  r->run_reconstruction<MerlinInterface>(mode);
1087  break;
1088  case CAMERA::TIMEPIX:
1089  r->run_reconstruction<TimepixInterface>(mode);
1090  break;
1091  default:
1092  break;
1093  }
1094 }
1095 
1096 void RICOM::run_connection_script(Ricom *ricom, MerlinSettings *merlin, const std::string &python_path)
1097 {
1098 
1099  int m_fr_total = ((ricom->nx + ricom->skip_row) * ricom->ny + ricom->skip_img) * ricom->rep;
1100 
1101  std::ofstream run_script("run_script.py");
1102  run_script << "from merlin_interface.merlin_interface import MerlinInterface" << std::endl;
1103  run_script << "m = MerlinInterface(tcp_ip = \"" << ricom->socket.ip << "\" , tcp_port=" << merlin->com_port << ")" << std::endl;
1104  run_script << "m.hvbias = " << merlin->hvbias << std::endl;
1105  run_script << "m.threshold0 = " << merlin->threshold0 << std::endl;
1106  run_script << "m.threshold1 = " << merlin->threshold1 << std::endl;
1107  run_script << "m.continuousrw = " << (int)merlin->continuousrw << std::endl;
1108  run_script << "m.counterdepth = " << ricom->camera.depth << std::endl;
1109  run_script << "m.acquisitiontime = " << merlin->dwell_time << std::endl;
1110  run_script << "m.acquisitionperiod = " << merlin->dwell_time << std::endl;
1111  run_script << "m.numframestoacquire = " << m_fr_total + 1 << std::endl; // Ich verstehe nicht warum, aber er werkt.
1112  run_script << "m.fileenable = " << (int)merlin->save << std::endl;
1113  run_script << "m.runheadless = " << (int)merlin->headless << std::endl;
1114  run_script << "m.fileformat = " << (int)merlin->raw * 2 << std::endl;
1115  run_script << "m.triggerstart = " << merlin->trigger << std::endl;
1116  run_script << "m.startacquisition()";
1117  run_script.close();
1118 
1119  std::string command = python_path + " run_script.py";
1120  int r = std::system(command.c_str());
1121  if (r != 0)
1122  {
1123  std::cout << "main::run_connection_script: Cannot execute python run_script. Shell exited with code " << r << std::endl;
1124  }
1125 }
void init(int n_threads, int limit)
void push_task(const T &task)
Camera_type type
Definition: Camera.h:44
Camera_model model
Definition: Camera.h:43
std::vector< int > v
Definition: Camera.h:51
std::vector< int > u
Definition: Camera.h:50
void init_uv_default()
Definition: Camera.cpp:20
std::atomic< bool > report_set
std::atomic< size_t > fr_count_i
std::atomic< size_t > fr_count
std::array< float, 2 > radius2
Definition: Ricom.h:129
std::array< float, 2 > radius
Definition: Ricom.h:128
std::vector< int > id_list
Definition: Ricom.h:130
void compute_detector(int nx_cam, int ny_cam, std::array< float, 2 > &offset)
Definition: Ricom.cpp:159
int k_area
Definition: Ricom.h:60
void include_filter()
Definition: Ricom.cpp:87
std::vector< float > kernel_x
Definition: Ricom.h:62
SDL_Surface * srf_kx
Definition: Ricom.h:66
void compute_kernel()
Definition: Ricom.cpp:21
std::vector< float > kernel_y
Definition: Ricom.h:63
int k_width_sym
Definition: Ricom.h:59
void draw_surfaces()
Definition: Ricom.cpp:128
bool b_filter
Definition: Ricom.h:57
std::vector< float > f_approx
Definition: Ricom.h:65
void approximate_frequencies(size_t n_im)
Definition: Ricom.cpp:111
float rotation
Definition: Ricom.h:61
SDL_Surface * srf_ky
Definition: Ricom.h:67
std::array< int, 2 > kernel_filter_frequency
Definition: Ricom.h:58
std::vector< float > kernel_filter
Definition: Ricom.h:64
void compute_filter()
Definition: Ricom.cpp:66
int kernel_size
Definition: Ricom.h:56
Definition: Ricom.h:152
int skip_img
Definition: Ricom.h:249
std::array< float, 2 > com_public
Definition: Ricom.h:235
SocketConnector socket
Definition: Ricom.h:216
int nxy
Definition: Ricom.h:245
int redraw_interval
Definition: Ricom.h:221
int last_y
Definition: Ricom.h:222
bool b_busy
Definition: Ricom.h:224
int cbed_cmap
Definition: Ricom.h:268
bool b_recompute_detector
Definition: Ricom.h:230
int e_mag_cmap
Definition: Ricom.h:270
int n_threads
Definition: Ricom.h:252
std::vector< float > stem_data
Definition: Ricom.h:239
void draw_stem_image()
Definition: Ricom.cpp:481
void draw_e_field_image()
Definition: Ricom.cpp:518
int rep
Definition: Ricom.h:246
bool update_offset
Definition: Ricom.h:225
bool b_plot2SDL
Definition: Ricom.h:229
std::vector< float > ricom_data
Definition: Ricom.h:238
SDL_Surface * srf_e_mag
Definition: Ricom.h:269
RICOM::modes mode
Definition: Ricom.h:219
std::atomic< bool > rescale_ricom
Definition: Ricom.h:258
int n_threads_max
Definition: Ricom.h:253
float fr_count
Definition: Ricom.h:256
std::vector< std::complex< float > > e_field_data
Definition: Ricom.h:240
std::string file_path
Definition: Ricom.h:217
void draw_ricom_image()
Definition: Ricom.cpp:443
void run_reconstruction(RICOM::modes mode)
Definition: Ricom.cpp:977
int nx
Definition: Ricom.h:243
Ricom_detector detector
Definition: Ricom.h:232
bool rc_quit
Definition: Ricom.h:261
int queue_size
Definition: Ricom.h:254
bool b_e_mag
Definition: Ricom.h:227
SDL_Surface * srf_ricom
Definition: Ricom.h:263
SDL_Surface * srf_cbed
Definition: Ricom.h:267
int fr_total
Definition: Ricom.h:247
std::vector< float > com_map_x
Definition: Ricom.h:236
ProgressMonitor * p_prog_mon
Definition: Ricom.h:223
int ricom_cmap
Definition: Ricom.h:264
enum CAMERA::Camera_model select_mode_by_file(const char *filename)
Definition: Ricom.cpp:1062
void reset()
Definition: Ricom.cpp:1055
bool b_plot_cbed
Definition: Ricom.h:228
float fr_freq
Definition: Ricom.h:255
SDL_Surface * srf_stem
Definition: Ricom.h:265
void plot_cbed(std::vector< T > *p_data)
Definition: Ricom.cpp:557
Ricom()
Definition: Ricom.cpp:255
bool b_recompute_kernel
Definition: Ricom.h:231
std::vector< float > com_map_y
Definition: Ricom.h:237
CAMERA::Camera_BASE camera
Definition: Ricom.h:218
void process_data(CAMERA::Camera< CameraInterface, CAMERA::FRAME_BASED > *camera)
Definition: Ricom.cpp:743
int skip_row
Definition: Ricom.h:248
std::atomic< bool > rescale_e_mag
Definition: Ricom.h:260
bool b_vSTEM
Definition: Ricom.h:226
int ny
Definition: Ricom.h:244
~Ricom()
Definition: Ricom.cpp:294
bool b_print2file
Definition: Ricom.h:220
std::array< float, 2 > offset
Definition: Ricom.h:234
std::atomic< bool > rescale_stem
Definition: Ricom.h:259
int stem_cmap
Definition: Ricom.h:266
Ricom_kernel kernel
Definition: Ricom.h:233
std::string ip
void init(Ricom_kernel kernel, int nx_ricom, int ny_ricom)
Definition: Ricom.cpp:190
std::vector< id_x_y > ids
Definition: Ricom.h:116
void shift(id_x_y &id_sft, int id, int shift)
Definition: Ricom.cpp:208
Definition: Ricom.h:98
int id
Definition: Ricom.h:100
bool valid
Definition: Ricom.h:101
id_x_y()
Definition: Ricom.h:102
Camera_model
Definition: Camera.h:28
@ TIMEPIX
Definition: Camera.h:30
@ MERLIN
Definition: Camera.h:29
@ FRAME_BASED
Definition: Camera.h:36
@ EVENT_BASED
Definition: Camera.h:37
Definition: Ricom.h:141
void run_connection_script(Ricom *r, MerlinSettings *merlin, const std::string &python_path)
Definition: Ricom.cpp:1096
modes
Definition: Ricom.h:143
@ FILE
Definition: Ricom.h:144
void run_ricom(Ricom *r, RICOM::modes mode)
Definition: Ricom.cpp:1081
void draw_pixel(SDL_Surface *surface, int x, int y, float val, int col_map)
Definition: GuiUtils.cpp:97