riCOM_cpp
This repository contains the C++ implementation of the riCOM (Real Time Centre Of Mass) algorithm for 4D Scanning electron microscopy.
Loading...
Searching...
No Matches
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
159void 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 //
184id_x_y::id_x_y(int id, bool valid)
185{
186 this->id = id;
187 this->valid = valid;
188};
189
190void 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
208void 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 //
219void 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 //
255Ricom::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), cbed_cmap_frames(1), 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
297template <typename T>
298void 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
316template <typename T>
317void 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
359template <typename T>
360void 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
387void 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
415void 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
456void 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
470void 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
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
494void 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
506void 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
532void 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
540}
541
542// set pixel in stem image srf_stem at location idx, idy to value corresponding
543// value in stem_data array
544void 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
556template <typename T>
557void 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 iy = 0; iy < camera.ny_cam; iy++)
580 {
581 int iy_t = camera.v[iy] * camera.nx_cam;
582 for (int ix = 0; ix < camera.nx_cam; ix++)
583 {
584 float vl_f = cbed_log[iy_t + camera.u[ix]];
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
593inline void Ricom::rescales_recomputes()
594{
596 {
598 b_recompute_detector = false;
599 }
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
629template <typename T, class CameraInterface>
630void 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)
639template <typename T>
640void 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);
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)
685template <typename T>
686void 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];
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
724void 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
742template <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;
750
751 // Start Thread Pool
752 if (n_threads > 1)
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
804template <typename T>
805void 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 {
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
825template <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
836
837 // Start Thread Pool
838 if (n_threads > 1)
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 behind to ensure events are accumulated
866 // (The event reader handles out-of-order events with its own margin)
867 int idxx = prog_mon.fr_count - nxy * img_num - 2;
868
869 ++prog_mon;
870 fr_count = prog_mon.fr_count;
871
872 if (b_plot_cbed && acc_cbed < cbed_cmap_frames)
873 {
874 // Reset frame only when starting new accumulation window
875 if (acc_cbed == 0)
876 {
877 frame.assign(camera_spec->nx_cam * camera_spec->ny_cam, 0);
878 acc_idx = fr_count;
879 }
880 camera_spec->read_frame_com_cbed(prog_mon.fr_count,
881 dose_map, sumx_map, sumy_map,
884 frame, acc_idx, cbed_cmap_frames,
885 first_frame, end_frame);
886 acc_cbed++;
887 }
888 else
889 {
890 camera_spec->read_frame_com(prog_mon.fr_count,
891 dose_map, sumx_map, sumy_map,
894 first_frame, end_frame);
895 }
896
897 if (idxx >= 0)
898 {
899
900 if (dose_map[idxx] == 0)
901 {
902 com_xy[0] = offset[0];
903 com_xy[1] = offset[1];
904 }
905 else
906 {
907 // Cast to float before division to preserve fractional precision
908 com_xy[0] = static_cast<float>(sumx_map[idxx]) / static_cast<float>(dose_map[idxx]);
909 com_xy[1] = static_cast<float>(sumy_map[idxx]) / static_cast<float>(dose_map[idxx]);
910 }
911 com_map_x[idxx] = com_xy[0];
912 com_map_y[idxx] = com_xy[1];
913 com_xy_sum[0] += com_xy[0];
914 com_xy_sum[1] += com_xy[1];
915
916 ix = idxx % nx;
917 iy = floor(idxx / nx);
918
919 if (n_threads > 1)
920 {
921 pool.push_task([=]
922 { icom(com_xy, ix, iy); });
923 }
924 else
925 {
926 icom(p_com_xy, ix, iy);
927 }
928 if (b_e_mag)
929 {
930 compute_electric_field(com_xy, idxx);
931 }
932 }
933
934 if (prog_mon.report_set)
935 {
936 update_surfaces(iy, p_frame);
937 if (b_plot_cbed)
938 {
939 // Only reset counter - frame will be reset when accumulation starts
940 acc_cbed = 0;
941 }
942 fr_freq = prog_mon.fr_freq;
943 rescales_recomputes();
944 for (int i = 0; i < 2; i++)
945 {
946 com_public[i] = com_xy_sum[i] / prog_mon.fr_count_i;
947 com_xy_sum[i] = 0;
948 }
949 prog_mon.reset_flags();
950 last_y = iy;
951 }
952
953 if (prog_mon.fr_count >= end_frame)
954 {
955 if (prog_mon.fr_count != fr_total_u)
956 {
957 img_num++;
958 first_frame = img_num * nxy;
959 end_frame = (img_num + 1) * nxy;
960 reinit_vectors_limits();
961 dose_map.assign(nxy, 0);
962 sumx_map.assign(nxy, 0);
963 sumy_map.assign(nxy, 0);
964 }
965
966 if (update_offset)
967 {
968 offset[0] = com_public[0];
969 offset[1] = com_public[1];
970 }
971 }
972
973 if (prog_mon.fr_count == fr_total_u || rc_quit)
974 {
975 pool.wait_for_completion();
976 p_prog_mon = nullptr;
977 return;
978 }
979 }
980 p_prog_mon = nullptr;
981}
982
983// Entrance function for Ricom_reconstructinon
984template <class CameraInterface>
986{
987 reset();
988 this->mode = mode;
989 b_busy = true;
990 // Initializations
991 nxy = nx * ny;
992 fr_total = nxy * rep;
993 fr_count = 0;
994
996 init_surface();
997
998 if (b_vSTEM)
999 {
1001 }
1002
1003 // Compute the integration Kenel
1005
1006 // Allocate the ricom image and COM arrays
1007 ricom_data.resize(nxy);
1008 update_list.init(kernel, nx, ny);
1009 com_map_x.resize(nxy);
1010 com_map_y.resize(nxy);
1011 stem_data.resize(nxy);
1012 e_field_data.resize(nxy);
1013
1014 // Run camera dependent pipeline
1015 // Implementations are in the Interface Wrappers (src/cameras/XXXWrapper.cpp), but essentially
1016 // they run Ricom::process_data<FRAME_BASED>() or Ricom::process_data<EVENT_BASED>()
1017 switch (camera.type)
1018 {
1020 {
1022 camera_interface.run(this);
1023 break;
1024 }
1026 {
1028 camera_interface.run(this);
1029 break;
1030 }
1031 }
1032 b_busy = false;
1033 std::cout << std::endl
1034 << "Reconstruction finished successfully." << std::endl;
1035}
1036
1037// Template specializations, necessary to avoid linker error
1038template void Ricom::run_reconstruction<MerlinInterface>(RICOM::modes);
1039template void Ricom::run_reconstruction<TimepixInterface>(RICOM::modes);
1040template void Ricom::process_data<uint8_t>(CAMERA::Camera<MerlinInterface, CAMERA::FRAME_BASED> *camera_spec);
1041template void Ricom::process_data<uint16_t>(CAMERA::Camera<MerlinInterface, CAMERA::FRAME_BASED> *camera_spec);
1043
1044// Helper functions
1045void Ricom::reset_limits()
1046{
1047 ricom_max = -FLT_MAX;
1048 ricom_min = FLT_MAX;
1049 stem_max = -FLT_MAX;
1050 stem_min = FLT_MAX;
1051}
1052
1053void Ricom::reinit_vectors_limits()
1054{
1055 ricom_data.assign(nxy, 0);
1056 stem_data.assign(nxy, 0);
1057 com_map_x.assign(nxy, 0);
1058 com_map_y.assign(nxy, 0);
1059 last_y = 0;
1060 reset_limits();
1061}
1062
1064{
1065 rc_quit = false;
1066 fr_freq = 0;
1067 reinit_vectors_limits();
1068}
1069
1071{
1072 file_path = filename;
1073 if (std::filesystem::path(filename).extension() == ".t3p")
1074 {
1075 mode = RICOM::FILE;
1076 return CAMERA::TIMEPIX;
1077 }
1078 else if (std::filesystem::path(filename).extension() == ".mib")
1079 {
1080 mode = RICOM::FILE;
1081 return CAMERA::MERLIN;
1082 }
1083 else
1084 {
1085 return CAMERA::TIMEPIX;
1086 }
1087}
1088
1090{
1091 switch (r->camera.model)
1092 {
1093 case CAMERA::MERLIN:
1094 r->run_reconstruction<MerlinInterface>(mode);
1095 break;
1096 case CAMERA::TIMEPIX:
1097 r->run_reconstruction<TimepixInterface>(mode);
1098 break;
1099 default:
1100 break;
1101 }
1102}
1103
1104void RICOM::run_connection_script(Ricom *ricom, MerlinSettings *merlin, const std::string &python_path)
1105{
1106
1107 int m_fr_total = ((ricom->nx + ricom->skip_row) * ricom->ny + ricom->skip_img) * ricom->rep;
1108
1109 std::ofstream run_script("run_script.py");
1110 run_script << "from merlin_interface.merlin_interface import MerlinInterface" << std::endl;
1111 run_script << "m = MerlinInterface(tcp_ip = \"" << ricom->socket.ip << "\" , tcp_port=" << merlin->com_port << ")" << std::endl;
1112 run_script << "m.hvbias = " << merlin->hvbias << std::endl;
1113 run_script << "m.threshold0 = " << merlin->threshold0 << std::endl;
1114 run_script << "m.threshold1 = " << merlin->threshold1 << std::endl;
1115 run_script << "m.continuousrw = " << (int)merlin->continuousrw << std::endl;
1116 run_script << "m.counterdepth = " << ricom->camera.depth << std::endl;
1117 run_script << "m.acquisitiontime = " << merlin->dwell_time << std::endl;
1118 run_script << "m.acquisitionperiod = " << merlin->dwell_time << std::endl;
1119 run_script << "m.numframestoacquire = " << m_fr_total + 1 << std::endl; // Ich verstehe nicht warum, aber er werkt.
1120 run_script << "m.fileenable = " << (int)merlin->save << std::endl;
1121 run_script << "m.runheadless = " << (int)merlin->headless << std::endl;
1122 run_script << "m.fileformat = " << (int)merlin->raw * 2 << std::endl;
1123 run_script << "m.triggerstart = " << merlin->trigger << std::endl;
1124 run_script << "m.startacquisition()";
1125 run_script.close();
1126
1127 std::string command = python_path + " run_script.py";
1128 int r = std::system(command.c_str());
1129 if (r != 0)
1130 {
1131 std::cout << "main::run_connection_script: Cannot execute python run_script. Shell exited with code " << r << std::endl;
1132 }
1133}
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:250
std::array< float, 2 > com_public
Definition Ricom.h:236
SocketConnector socket
Definition Ricom.h:216
int nxy
Definition Ricom.h:246
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:269
bool b_recompute_detector
Definition Ricom.h:231
int e_mag_cmap
Definition Ricom.h:271
int n_threads
Definition Ricom.h:253
std::vector< float > stem_data
Definition Ricom.h:240
void draw_stem_image()
Definition Ricom.cpp:481
void draw_e_field_image()
Definition Ricom.cpp:518
int rep
Definition Ricom.h:247
bool update_offset
Definition Ricom.h:225
bool b_plot2SDL
Definition Ricom.h:230
std::vector< float > ricom_data
Definition Ricom.h:239
SDL_Surface * srf_e_mag
Definition Ricom.h:270
RICOM::modes mode
Definition Ricom.h:219
std::atomic< bool > rescale_ricom
Definition Ricom.h:259
int n_threads_max
Definition Ricom.h:254
float fr_count
Definition Ricom.h:257
int cbed_cmap_frames
Definition Ricom.h:229
std::vector< std::complex< float > > e_field_data
Definition Ricom.h:241
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:985
int nx
Definition Ricom.h:244
Ricom_detector detector
Definition Ricom.h:233
bool rc_quit
Definition Ricom.h:262
int queue_size
Definition Ricom.h:255
bool b_e_mag
Definition Ricom.h:227
SDL_Surface * srf_ricom
Definition Ricom.h:264
SDL_Surface * srf_cbed
Definition Ricom.h:268
int fr_total
Definition Ricom.h:248
std::vector< float > com_map_x
Definition Ricom.h:237
ProgressMonitor * p_prog_mon
Definition Ricom.h:223
int ricom_cmap
Definition Ricom.h:265
enum CAMERA::Camera_model select_mode_by_file(const char *filename)
Definition Ricom.cpp:1070
void reset()
Definition Ricom.cpp:1063
bool b_plot_cbed
Definition Ricom.h:228
float fr_freq
Definition Ricom.h:256
SDL_Surface * srf_stem
Definition Ricom.h:266
void plot_cbed(std::vector< T > *p_data)
Definition Ricom.cpp:557
Ricom()
Definition Ricom.cpp:255
bool b_recompute_kernel
Definition Ricom.h:232
std::vector< float > com_map_y
Definition Ricom.h:238
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:249
std::atomic< bool > rescale_e_mag
Definition Ricom.h:261
bool b_vSTEM
Definition Ricom.h:226
int ny
Definition Ricom.h:245
~Ricom()
Definition Ricom.cpp:294
bool b_print2file
Definition Ricom.h:220
std::array< float, 2 > offset
Definition Ricom.h:235
std::atomic< bool > rescale_stem
Definition Ricom.h:260
int stem_cmap
Definition Ricom.h:267
Ricom_kernel kernel
Definition Ricom.h:234
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:1104
modes
Definition Ricom.h:143
@ FILE
Definition Ricom.h:144
void run_ricom(Ricom *r, RICOM::modes mode)
Definition Ricom.cpp:1089
void draw_pixel(SDL_Surface *surface, int x, int y, float val, int col_map)
Definition GuiUtils.cpp:97