STIR 6.4.0
DataSymmetriesForBins_PET_CartesianGrid.inl
Go to the documentation of this file.
1//
2//
14/*
15 Copyright (C) 2000 PARAPET partners
16 Copyright (C) 2000- 2009, Hammersmith Imanet Ltd
17 Copyright 2017 ETH Zurich, Institute of Particle Physics and Astrophysics
18
19 This file is part of STIR.
20
21 SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license
22
23 See STIR/LICENSE.txt for details
24
25 Modification history:
26
27 KT 30/05/2002 added possibility for reduced symmetry in view_num
28*/
31#include "stir/ProjDataInfoGeneric.h"
32#include "stir/warning.h"
33#include "stir/error.h"
34
35START_NAMESPACE_STIR
36
37#if 0
38const DiscretisedDensityOnCartesianGrid<3,float> *
39DataSymmetriesForBins_PET_CartesianGrid::
40cartesian_grid_info_ptr() const
41{
42 // we can use static_cast here, as the constructor already checked that it is type-safe
43 return static_cast<const DiscretisedDensityOnCartesianGrid<3,float> *>
44 (image_info_ptr.get());
45}
46#endif
47
48float
50{
51 return static_cast<float>(num_planes_per_axial_pos[segment_num]);
52}
53
54float
56{
57 return static_cast<float>(num_planes_per_scanner_ring);
58}
59
60float
61DataSymmetriesForBins_PET_CartesianGrid::get_axial_pos_to_z_offset(const int segment_num) const
62{
63 return axial_pos_to_z_offset[segment_num];
64}
65
66int
67DataSymmetriesForBins_PET_CartesianGrid::find_transform_z(const int segment_num, const int axial_pos_num) const
68{
69 const float delta = this->deltas[segment_num];
70 int transform_z;
71 // cylindrical implementaion
72 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Cylindrical")
73 {
74
75 // Find symmetric value in Z by 'mirroring' it around the centre z of the LOR:
76 // Z+Q = 2*centre_of_LOR_in_image_coordinates == transform_z
77 {
78 // first compute it as floating point (although it has to be an int really)
79 const float transform_z_float = (2 * num_planes_per_axial_pos[segment_num] * (axial_pos_num)
80 + num_planes_per_scanner_ring * delta + 2 * axial_pos_to_z_offset[segment_num]);
81 // now use rounding to be safe
82 transform_z = (int)floor(transform_z_float + 0.5);
83 assert(fabs(transform_z - transform_z_float) < 10E-4);
84 }
85 }
86 // block implementaion
87 else if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "BlocksOnCylindrical")
88 {
89
90 // Find symmetric value in Z by 'mirroring' it around the centre z of the LOR:
91 // Z+Q = 2*centre_of_LOR_in_image_coordinates == transform_z
92 {
93 // first compute it as floating point (although it has to be an int really)
94 const float transform_z_float = (2 * num_planes_per_axial_pos[segment_num] * (axial_pos_num)
95 + num_planes_per_scanner_ring * delta + 2 * axial_pos_to_z_offset[segment_num]);
96 // now use rounding to be safe
97 transform_z = (int)floor(transform_z_float + 0.5);
98 assert(fabs(transform_z - transform_z_float) < 10E-4);
99 }
100 }
101 // generic implementation
102 else
103 {
104 // Find symmetric value in Z by 'mirroring' it around the centre z of the LOR:
105 // Z+Q = 2*centre_of_LOR_in_image_coordinates == transform_z
106 {
107 // first compute it as floating point (although it has to be an int really)
108 const float transform_z_float = (2 * num_planes_per_axial_pos[segment_num] * (axial_pos_num)
109 + num_planes_per_scanner_ring * delta + 2 * axial_pos_to_z_offset[segment_num]);
110 // now use rounding to be safe
111 int transform_z = (int)floor(transform_z_float + 0.5);
112 assert(fabs(transform_z - transform_z_float) < 10E-4);
113
114 return transform_z;
115 }
116 }
117 return transform_z;
118}
119
120SymmetryOperation*
121DataSymmetriesForBins_PET_CartesianGrid::find_sym_op_bin0(int segment_num, int view_num, int axial_pos_num) const
122{
123 // cylindrical implementaion
124 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Cylindrical")
125 {
126 // note: if do_symmetry_shift_z==true, then basic axial_pos_num will be 0
127 const int transform_z = find_transform_z(abs(segment_num), do_symmetry_shift_z ? 0 : axial_pos_num);
128
129 // If doing z shifts, set the axial_pos_shift to axial_pos_num, else, set to 0
130 const int axial_pos_shift = do_symmetry_shift_z ? axial_pos_num : 0;
131
132 const int z_shift = do_symmetry_shift_z ? num_planes_per_axial_pos[segment_num] * axial_pos_num : 0;
133
134 const int view180 = num_views;
135
136 // TODO get rid of next 2 restrictions
137 assert(!do_symmetry_180degrees_min_phi || view_num >= 0);
138 assert(!do_symmetry_180degrees_min_phi || view_num < num_views);
139
140#ifndef NDEBUG
141 // This variable is only used in assert() at the moment, so avoid compiler
142 // warning by defining it only when in debug mode
143 const int view0 = 0;
144#endif
145 const int view135 = view180 / 4 * 3;
146 const int view90 = view180 / 2;
147 const int view45 = view180 / 4;
148
149 if (do_symmetry_90degrees_min_phi && view_num > view90 && view_num <= view135)
150 { //(90, 135 ]
151 if (!do_symmetry_swap_segment || segment_num >= 0)
152 return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_shift, z_shift);
153 else
154 return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq(
155 view180, axial_pos_shift, z_shift, transform_z); // seg < 0
156 }
157 else if (do_symmetry_90degrees_min_phi && view_num > view45 && view_num <= view90)
158 { // [ 45, 90]
159 if (!do_symmetry_swap_segment || segment_num >= 0)
160 return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq(view180, axial_pos_shift, z_shift, transform_z);
161 else
162 return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(
163 view180, axial_pos_shift, z_shift); // seg < 0 //KT???????????? different for view90, TODO
164 }
165 else if (do_symmetry_180degrees_min_phi && view_num > view90 /* && view_num <= view180 */)
166 { // (135, 180) but (90,180) for reduced symmetry case
167 if (!do_symmetry_swap_segment || segment_num >= 0)
168 return new SymmetryOperation_PET_CartesianGrid_swap_xmx_zq(view180, axial_pos_shift, z_shift, transform_z);
169 else
170 return new SymmetryOperation_PET_CartesianGrid_swap_xmx(view180, axial_pos_shift, z_shift); // seg < 0
171 }
172 else
173 {
174 assert(!do_symmetry_90degrees_min_phi || (view_num >= view0 && view_num <= view45));
175 assert(!do_symmetry_180degrees_min_phi || (view_num >= view0 && view_num <= view90));
176 if (do_symmetry_swap_segment && segment_num < 0)
177 return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_shift, z_shift, transform_z);
178 else
179 {
180 if (z_shift == 0)
181 return new TrivialSymmetryOperation();
182 else
183 return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_shift, z_shift);
184 }
185 }
186 }
187 // block implementaion
188 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "BlocksOnCylindrical")
189 {
190 if (do_symmetry_90degrees_min_phi || do_symmetry_swap_segment || do_symmetry_swap_s || do_symmetry_180degrees_min_phi)
191 {
192 warning("Currently, no symmetry is implemented for block geometry.\n");
193 return new TrivialSymmetryOperation();
194 }
195
196 if (do_symmetry_shift_z)
197 {
198 Bin basic_bin(segment_num, view_num, axial_pos_num, 0);
199 find_basic_bin(basic_bin);
200 const int z_shift = num_planes_per_axial_pos[segment_num] * (axial_pos_num - basic_bin.axial_pos_num());
201
202 if (z_shift == 0)
203 return new TrivialSymmetryOperation();
204 else
205 return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_num, z_shift);
206 }
207
208 if (!do_symmetry_90degrees_min_phi && !do_symmetry_swap_segment && !do_symmetry_swap_s && !do_symmetry_180degrees_min_phi
209 && !do_symmetry_shift_z)
210 {
211 return new TrivialSymmetryOperation();
212 }
213 }
214 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Generic")
215 {
216 // No symmetry is implemented for generic scanner
217 return new TrivialSymmetryOperation();
218 }
219 return new TrivialSymmetryOperation();
220}
221
222// from symmetries
223SymmetryOperation*
224DataSymmetriesForBins_PET_CartesianGrid::find_sym_op_general_bin(int s, int segment_num, int view_num, int axial_pos_num) const
225{
226 // cylindrical implementaion
227 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Cylindrical")
228 {
229 // note: if do_symmetry_shift_z==true, then basic axial_pos_num will be 0
230 const int transform_z = find_transform_z(abs(segment_num), do_symmetry_shift_z ? 0 : axial_pos_num);
231
232 // If doing z shifts, set the axial_pos_shift to axial_pos_num, else, set to 0
233 const int axial_pos_shift = do_symmetry_shift_z ? axial_pos_num : 0;
234
235 const int z_shift = do_symmetry_shift_z ? num_planes_per_axial_pos[segment_num] * axial_pos_num : 0;
236
237 // TODO get rid of next 2 restrictions
238 assert(!do_symmetry_180degrees_min_phi || view_num >= 0);
239 assert(!do_symmetry_180degrees_min_phi || view_num < num_views);
240
241 const int view180 = num_views;
242#ifndef NDEBUG
243 // This variable is only used in assert() at the moment, so avoid compiler
244 // warning by defining it only when in debug mode
245 const int view0 = 0;
246#endif
247
248 const int view135 = view180 / 4 * 3;
249 const int view90 = view180 / 2;
250 const int view45 = view180 / 4;
251
252 if (do_symmetry_90degrees_min_phi && view_num > view90 && view_num <= view135)
253 { //(90, 135 ]
254 if (!do_symmetry_swap_segment || segment_num > 0)
255 { // pos_plus90
256 if (!do_symmetry_swap_s || s > 0)
257 return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_shift, z_shift);
258 else
259 return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq(
260 view180, axial_pos_shift, z_shift, transform_z); // s < 0
261 }
262 else // neg_plus90
264 if (segment_num < 0)
265 {
266 if (!do_symmetry_swap_s || s > 0)
267 return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq(view180, axial_pos_shift, z_shift, transform_z);
268 else
269 return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx(view180, axial_pos_shift, z_shift);
270 }
271 else
272 { // segment_num == 0
273 if (!do_symmetry_swap_s || s > 0)
274 return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_shift, z_shift);
275 else
276 return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx(view180, axial_pos_shift, z_shift);
277 }
278 }
279 else if (do_symmetry_90degrees_min_phi && view_num > view45 && view_num <= view90) // [ 45, 90]
280 {
281 if (!do_symmetry_swap_segment || segment_num > 0)
282 {
283 if (!do_symmetry_swap_s || s > 0)
284 return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq(view180, axial_pos_shift, z_shift, transform_z);
285 else
286 return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(view180, axial_pos_shift, z_shift);
287 }
288 else if (segment_num < 0)
289 { // {//101 segment_num < 0
290 if (!do_symmetry_swap_s || s > 0)
291 return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(view180, axial_pos_shift, z_shift);
292 else
293 return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq(view180, axial_pos_shift, z_shift, transform_z);
294 }
295 else // segment_num == 0
296 {
297 if (!do_symmetry_swap_s || s > 0)
298 return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(view180, axial_pos_shift, z_shift);
299 else
300 return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(view180, axial_pos_shift, z_shift);
301 }
302 }
303 else if (do_symmetry_180degrees_min_phi
304 && view_num > view90 /* && view_num <= view180 */) // (135, 180) but (90,180) for reduced symmetry case
305 {
306 if (!do_symmetry_swap_segment || segment_num > 0)
307 {
308 if (!do_symmetry_swap_s || s > 0)
309 return new SymmetryOperation_PET_CartesianGrid_swap_xmx_zq(view180, axial_pos_shift, z_shift, transform_z);
310 else
311 return new SymmetryOperation_PET_CartesianGrid_swap_ymy(view180, axial_pos_shift, z_shift); // s <= 0
312 }
313 else // if ( segment_num < 0 )
314 { // segment_num <= 0
315 if (!do_symmetry_swap_s || s > 0)
316 return new SymmetryOperation_PET_CartesianGrid_swap_xmx(view180, axial_pos_shift, z_shift);
317 else
318 return new SymmetryOperation_PET_CartesianGrid_swap_ymy_zq(view180, axial_pos_shift, z_shift, transform_z);
319 } // segment_num == 0
320 // /*else{ if ( !do_symmetry_swap_s || s > 0 ) return new SymmetryOperation_PET_CartesianGrid_swap_xmx(); else
321 // return new SymmetryOperation_PET_CartesianGrid_swap_ymy(view180, axial_pos_shift, z_shift);}*/
322 }
323 else
324 {
325 assert(!do_symmetry_90degrees_min_phi || (view_num >= view0 && view_num <= view45));
326 assert(!do_symmetry_180degrees_min_phi || (view_num >= view0 && view_num <= view90));
327 if (!do_symmetry_swap_segment || segment_num > 0)
328 {
329 if (do_symmetry_swap_s && s < 0)
330 return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq(view180, axial_pos_shift, z_shift, transform_z);
331 else
332 {
333 if (z_shift == 0)
334 return new TrivialSymmetryOperation();
335 else
336 return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_shift, z_shift);
337 }
338 }
339 else if (segment_num < 0)
340 {
341 /*KT if ( s == 0)
342 return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_shift, z_shift, transform_z);
343 else*/
344 if (do_symmetry_swap_s && s < 0)
345 return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy(view180, axial_pos_shift, z_shift);
346 else
347 return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_shift, z_shift, transform_z); // s > 0
348 }
349 else // segment_num = 0
350 {
351 if (do_symmetry_swap_s && s < 0)
352 return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy(view180, axial_pos_shift, z_shift);
353 else
354 {
355 if (z_shift == 0)
356 return new TrivialSymmetryOperation();
357 else
358 return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_shift, z_shift);
359 }
360 }
361 }
362 }
363 // block implementaion
364 // the implementation is as the above function for the current status of block symmetry.
365 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "BlocksOnCylindrical")
366 {
367 if (do_symmetry_90degrees_min_phi || do_symmetry_swap_segment || do_symmetry_swap_s || do_symmetry_180degrees_min_phi)
368 {
369 warning("Currently, only symmetry along z is implemented for block geometry.\n");
370 return new TrivialSymmetryOperation();
371 }
372 if (do_symmetry_shift_z)
373 {
374 Bin basic_bin(segment_num, view_num, axial_pos_num, s);
375 find_basic_bin(basic_bin);
376 const int z_shift = num_planes_per_axial_pos[segment_num] * (axial_pos_num - basic_bin.axial_pos_num());
377
378 if (z_shift == 0)
379 return new TrivialSymmetryOperation();
380 else
381 return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_num, z_shift);
382 }
383 if (!do_symmetry_90degrees_min_phi && !do_symmetry_swap_segment && !do_symmetry_swap_s && !do_symmetry_180degrees_min_phi
384 && !do_symmetry_shift_z)
385 {
386 return new TrivialSymmetryOperation();
387 }
388 }
389 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Generic")
390 {
391 // No symmetry is implemented for generic scanner
392 return new TrivialSymmetryOperation();
393 }
394
395 return new TrivialSymmetryOperation();
396}
397
398bool
400{
401 bool change = false;
402 // TODO get rid of next 2 restrictions
403 assert(!do_symmetry_180degrees_min_phi || v_s.view_num() >= 0);
404 assert(!do_symmetry_180degrees_min_phi || v_s.view_num() < num_views);
405
406 // const int view0= 0;
407 const int view90 = num_views >> 1;
408 const int view45 = view90 >> 1;
409 const int view135 = view90 + view45;
410
411 if (do_symmetry_swap_segment && v_s.segment_num() < 0)
412 {
413 v_s.segment_num() = -v_s.segment_num();
414 change = true;
415 }
416
417 if (do_symmetry_90degrees_min_phi)
418 {
419 // if ( v_s.view_num() == num_views ) v_s.view_num() =0; // KT 30/05/2002 disabled as it should never happen
420 // else
421 if (v_s.view_num() >= view135)
422 {
423 v_s.view_num() = num_views - v_s.view_num();
424 return true;
425 }
426 else if (v_s.view_num() >= view90)
427 {
428 v_s.view_num() = v_s.view_num() - view90;
429 return true;
430 }
431 else if (v_s.view_num() > view45)
432 {
433 v_s.view_num() = view90 - v_s.view_num();
434 return true;
435 }
436 }
437 else if (do_symmetry_180degrees_min_phi)
438 {
439 if (v_s.view_num() > view90)
440 {
441 v_s.view_num() = num_views - v_s.view_num();
442 return true;
443 }
444 }
445
446 return change;
447}
448
449bool
451 int& segment_num, int& view_num, int& axial_pos_num, int& tangential_pos_num, int& timing_pos_num) const
452{
453 bool change = false;
454 // cylindrical implementaion
455 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Cylindrical")
456 {
457 ViewSegmentNumbers v_s(view_num, segment_num);
458
459 change = find_basic_view_segment_numbers(v_s);
460
461 view_num = v_s.view_num();
462 segment_num = v_s.segment_num();
463
464 if (do_symmetry_swap_s && tangential_pos_num < 0)
465 {
466 // when swap_s, must invert timing pos for lor probs. Symmetry operation should correct bin
467 tangential_pos_num *= -1;
468 timing_pos_num *= -1;
469 change = true;
470 }
471 if (do_symmetry_shift_z && axial_pos_num != 0)
472 {
473 axial_pos_num = 0;
474 change = true;
475 }
476
477 return change;
478 }
479
480 // block implementaion
481 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "BlocksOnCylindrical")
482 {
483 /*
484 ax_pos_num = (ring1 + ring2 - ax_pos_num_offset[seg_num])*num_ax_pos_per_ring_inc(seg_num)/2
485 ax_pos_num_offset = num_rings - 1 - (max_ax_pos_num + min_ax_pos_num)/num_ax_pos_per_ring_inc(seg_num)
486 Then
487 ax_pos_num = (ring1 + ring2 - num_rings + 1)*ax_pos_inc/2 - (max_ax_pos_num + min_ax_pos_num)/2
488 and
489 max_ax_pos_num + min_ax_pos_num = (num_ax_pos_per_seg -1) + 0 = num_rings - seg_num - 1
490 Then
491 ax_pos_num = (ring1 + ring2 - num_rings + 1)*ax_pos_inc/2 - (num_rings - seg_num - 1)/2
492 if ax_pos_inc == 1
493 ax_pos_num = (ring1 + ring2 - seg_num)/2
494 */
495 const ProjDataInfoBlocksOnCylindrical* proj_data_info_blk_ptr
496 = static_cast<const ProjDataInfoBlocksOnCylindrical*>(proj_data_info_ptr.get());
497 if (do_symmetry_shift_z)
498 {
499 int ring1, ring2;
500 proj_data_info_blk_ptr->get_ring_pair_for_segment_axial_pos_num(ring1, ring2, segment_num, axial_pos_num);
501 // to check
502 // std::cout<<"before seg, ax, r1, r2 = "<<segment_num<<"\t"<<axial_pos_num<<"\t"<<ring1<<"\t"<<ring2<<"\n";
503
504 int axial_crys_diff = ring2 - ring1;
505 int num_axial_crys_per_block = proj_data_info_ptr->get_scanner_ptr()->get_num_axial_crystals_per_block();
506 int axial_blk_diff = ring2 / num_axial_crys_per_block - ring1 / num_axial_crys_per_block;
507
508 if (axial_crys_diff >= 0)
509 { // seg_num > =0
510 if (axial_crys_diff % num_axial_crys_per_block == 0)
511 { // In this case, axial block difference can be only equal to axial_crys_diff/num_axial_crys_per_block. So we
512 // only have one type of related bins
513 // basic bin will be the first lor from the corresponding group
514 ring1 = (ring1 / num_axial_crys_per_block) * num_axial_crys_per_block;
515 ring2 = ring1 + axial_crys_diff;
516 }
517 else
518 { /* In this case, axial block difference can be equal to axial_crys_diff/num_axial_crys_per_block
519 or one less.
520 So we can have two types of of related bins*/
521 if (axial_blk_diff == axial_crys_diff / num_axial_crys_per_block)
522 { // basic bin will be the first lor from the corresponding group
523 ring1 = (ring1 / num_axial_crys_per_block) * num_axial_crys_per_block;
524 ring2 = ring1 + axial_crys_diff;
525 }
526 else if (axial_blk_diff > axial_crys_diff / num_axial_crys_per_block)
527 { // basic bin will be the last lor from the corresponding group
528 ring1 = (ring1 / num_axial_crys_per_block) * num_axial_crys_per_block + num_axial_crys_per_block - 1;
529 ring2 = ring1 + axial_crys_diff;
530 }
531 }
532 }
533 else if (axial_crys_diff < 0)
534 { // seg_num < 0
535 if (abs(axial_crys_diff) % num_axial_crys_per_block == 0)
536 { // In this case, axial block difference can be only equal to axial_crys_diff/num_axial_crys_per_block. So we
537 // only have one type of related bins
538 // basic bin will be the first lor from the corresponding group
539 ring2 = (ring2 / num_axial_crys_per_block) * num_axial_crys_per_block;
540 ring1 = ring2 - axial_crys_diff;
541 }
542 else
543 { /* In this case, axial block difference can be equal to axial_crys_diff/num_axial_crys_per_block
544 or one less.
545 So we can have two types of of related bins*/
546 if (abs(axial_blk_diff) == abs(axial_crys_diff) / num_axial_crys_per_block)
547 { // basic bin will be the first lor from the corresponding group
548 ring2 = (ring2 / num_axial_crys_per_block) * num_axial_crys_per_block;
549 ring1 = ring2 - axial_crys_diff;
550 }
551 else if (abs(axial_blk_diff) > abs(axial_crys_diff) / num_axial_crys_per_block)
552 { // basic bin will be the last lor from the corresponding group
553 ring2 = (ring2 / num_axial_crys_per_block) * num_axial_crys_per_block + num_axial_crys_per_block - 1;
554 ring1 = ring2 - axial_crys_diff;
555 }
556 }
557 }
558
559 int segment_num_temp, axial_pos_num_temp;
560 proj_data_info_blk_ptr->get_segment_axial_pos_num_for_ring_pair(segment_num_temp, axial_pos_num_temp, ring1, ring2);
561
562 // to check
563 // std::cout<<"after seg, ax, r1, r2 = "<<segment_num_temp<<"\t"<<axial_pos_num_temp<<"\t"<<ring1<<"\t"<<ring2<<"\n";
564
565 if (segment_num_temp != segment_num)
566 error("segment number shouldn't change in basic bin when implementing only symmetry in z.\n"
567 "segment_num = %d while segment_num_temp = %d \n",
568 segment_num,
569 segment_num_temp);
570 else if (axial_pos_num_temp != axial_pos_num)
571 {
572 axial_pos_num = axial_pos_num_temp;
573 change = true;
574 }
575 }
576 }
577 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Generic")
578 { // same procedure as for the block implementation above
579 const ProjDataInfoGeneric* proj_data_info_gen_ptr = static_cast<const ProjDataInfoGeneric*>(proj_data_info_ptr.get());
580 if (do_symmetry_shift_z)
581 {
582 int ring1, ring2;
583 proj_data_info_gen_ptr->get_ring_pair_for_segment_axial_pos_num(ring1, ring2, segment_num, axial_pos_num);
584
585 int axial_crys_diff = ring2 - ring1;
586 int num_axial_crys_per_block = proj_data_info_ptr->get_scanner_ptr()->get_num_axial_crystals_per_block();
587 int axial_blk_diff = ring2 / num_axial_crys_per_block - ring1 / num_axial_crys_per_block;
588
589 if (axial_crys_diff >= 0)
590 { // seg_num > 0
591 if (axial_crys_diff % num_axial_crys_per_block == 0)
592 { // axial block difference can be only axial_crys_diff/num_axial_crys_per_block. So we only have one type of
593 // related bins
594 // basic bin will be the first lor from the corresponding group
595 ring1 = (ring1 / num_axial_crys_per_block) * num_axial_crys_per_block;
596 ring2 = ring1 + axial_crys_diff;
597 }
598 else
599 { /* axial block difference can be axial_crys_diff/num_axial_crys_per_block
600 or one less.
601 So we can have two types of of related bins*/
602 if (axial_blk_diff == axial_crys_diff / num_axial_crys_per_block)
603 { // basic bin will be the first lor from the corresponding group
604 ring1 = (ring1 / num_axial_crys_per_block) * num_axial_crys_per_block;
605 ring2 = ring1 + axial_crys_diff;
606 }
607 else if (axial_blk_diff > axial_crys_diff / num_axial_crys_per_block)
608 { // basic bin will be the last lor from the corresponding group
609 ring1 = (ring1 / num_axial_crys_per_block) * num_axial_crys_per_block + num_axial_crys_per_block - 1;
610 ring2 = ring1 + axial_crys_diff;
611 }
612 }
613 }
614 else if (axial_crys_diff < 0)
615 { // seg_num < 0
616 if (abs(axial_crys_diff) % num_axial_crys_per_block == 0)
617 { // axial block difference can be only axial_crys_diff/num_axial_crys_per_block. So we only have one type of
618 // related bins
619 // basic bin will be the first lor from the corresponding group
620 ring2 = (ring2 / num_axial_crys_per_block) * num_axial_crys_per_block;
621 ring1 = ring2 - axial_crys_diff;
622 }
623 else
624 { /* axial block difference can be axial_crys_diff/num_axial_crys_per_block
625 or one less.
626 So we can have two types of of related bins*/
627 if (abs(axial_blk_diff) == abs(axial_crys_diff) / num_axial_crys_per_block)
628 { // basic bin will be the first lor from the corresponding group
629 ring2 = (ring2 / num_axial_crys_per_block) * num_axial_crys_per_block;
630 ring1 = ring2 - axial_crys_diff;
631 }
632 else if (abs(axial_blk_diff) > abs(axial_crys_diff) / num_axial_crys_per_block)
633 { // basic bin will be the last lor from the corresponding group
634 ring2 = (ring2 / num_axial_crys_per_block) * num_axial_crys_per_block + num_axial_crys_per_block - 1;
635 ring1 = ring2 - axial_crys_diff;
636 }
637 }
638 }
639
640 int segment_num_temp, axial_pos_num_temp;
641 proj_data_info_gen_ptr->get_segment_axial_pos_num_for_ring_pair(segment_num_temp, axial_pos_num_temp, ring1, ring2);
642
643 if (segment_num_temp != segment_num)
644 error("segment number shouldn't change in basic bin when implementing only symmetry in z.\n"
645 "segment_num = %d while segment_num_temp = %d \n",
646 segment_num,
647 segment_num_temp);
648 else if (axial_pos_num_temp != axial_pos_num)
649 {
650 axial_pos_num = axial_pos_num_temp;
651 change = true;
652 }
653 }
654 }
655 return change;
656}
657
658bool
663
664// TODO, optimise
665unique_ptr<SymmetryOperation>
667{
668 unique_ptr<SymmetryOperation> sym_op(
669 (b.tangential_pos_num() == 0)
670 ? find_sym_op_bin0(b.segment_num(), b.view_num(), b.axial_pos_num())
671 : find_sym_op_general_bin(b.tangential_pos_num(), b.segment_num(), b.view_num(), b.axial_pos_num()));
673 return sym_op;
674}
675
676int
678{
679 int num = do_symmetry_180degrees_min_phi && (vs.view_num() % (num_views / 2)) != 0 ? 2 : 1;
680 if (do_symmetry_90degrees_min_phi && (vs.view_num() % (num_views / 2)) != num_views / 4)
681 num *= 2;
682 if (do_symmetry_swap_segment && vs.segment_num() != 0)
683 num *= 2;
684 return num;
685}
686
687int
689{
690 int num = 0;
691 // cylindrical implementaion
692 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Cylindrical")
693 {
694 num = do_symmetry_180degrees_min_phi && (b.view_num() % (num_views / 2)) != 0 ? 2 : 1;
695 if (do_symmetry_90degrees_min_phi && (b.view_num() % (num_views / 2)) != num_views / 4)
696 num *= 2;
697 if (do_symmetry_swap_segment && b.segment_num() != 0)
698 num *= 2;
699
700 if (do_symmetry_swap_s && b.tangential_pos_num() != 0)
701 num *= 2;
702
703 if (do_symmetry_shift_z)
704 num *= proj_data_info_ptr->get_num_axial_poss(b.segment_num());
705 }
706
707 // block implementaion
708 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "BlocksOnCylindrical")
709 {
710 const ProjDataInfoBlocksOnCylindrical* proj_data_info_blk_ptr
711 = static_cast<const ProjDataInfoBlocksOnCylindrical*>(proj_data_info_ptr.get());
712 if (do_symmetry_shift_z)
713 {
714 int ring1, ring2;
715 proj_data_info_blk_ptr->get_ring_pair_for_segment_axial_pos_num(ring1, ring2, b.segment_num(), b.axial_pos_num());
716 int axial_crys_diff = ring1 - ring2;
717 int num_axial_crys_per_block = proj_data_info_ptr->get_scanner_ptr()->get_num_axial_crystals_per_block();
718 int axial_blk_diff = ring1 / num_axial_crys_per_block - ring2 / num_axial_crys_per_block;
719
720 if (axial_blk_diff == axial_crys_diff / num_axial_crys_per_block)
721 {
722 num = num_axial_crys_per_block - abs(axial_crys_diff) % num_axial_crys_per_block;
723 }
724 else if (axial_blk_diff > axial_crys_diff / num_axial_crys_per_block)
725 {
726 num = abs(axial_crys_diff) % num_axial_crys_per_block;
727 }
728 }
729 }
730 // generic implementation
731 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Generic")
732 {
733 num = 1;
734 }
735
736 return num;
737}
738
739void
741 const Bin& b,
742 const int min_axial_pos_num,
743 const int max_axial_pos_num,
744 const int min_tangential_pos_num,
745 const int max_tangential_pos_num) const
746{
747 // cylindrical implementaion
748 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Cylindrical")
749 {
750 for (int axial_pos_num = do_symmetry_shift_z ? min_axial_pos_num : b.axial_pos_num();
751 axial_pos_num <= (do_symmetry_shift_z ? max_axial_pos_num : b.axial_pos_num());
752 ++axial_pos_num)
753 {
754 if (b.tangential_pos_num() >= min_tangential_pos_num && b.tangential_pos_num() <= max_tangential_pos_num)
755 ax_tang_poss.push_back(AxTangPosNumbers(axial_pos_num, b.tangential_pos_num()));
756 if (do_symmetry_swap_s && b.tangential_pos_num() != 0 && -b.tangential_pos_num() >= min_tangential_pos_num
757 && -b.tangential_pos_num() <= max_tangential_pos_num)
758 ax_tang_poss.push_back(AxTangPosNumbers(axial_pos_num, -b.tangential_pos_num()));
759 }
760 }
761
762 // block implementaion
763 // currently it only saves related bins according to z-symmetry
764 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "BlocksOnCylindrical")
765 {
766 for (int axial_pos_num = do_symmetry_shift_z ? min_axial_pos_num : b.axial_pos_num();
767 axial_pos_num <= (do_symmetry_shift_z ? max_axial_pos_num : b.axial_pos_num());
768 ++axial_pos_num)
769 {
770 if (b.tangential_pos_num() >= min_tangential_pos_num && b.tangential_pos_num() <= max_tangential_pos_num)
771 {
772 Bin basic_bin(b);
773 find_basic_bin(basic_bin);
774 Bin bin_temp(b.segment_num(), b.view_num(), axial_pos_num, b.tangential_pos_num());
775 find_basic_bin(bin_temp);
776 if (basic_bin == bin_temp)
777 ax_tang_poss.push_back(AxTangPosNumbers(axial_pos_num, b.tangential_pos_num()));
778 }
779 }
780 }
781 if (proj_data_info_ptr->get_scanner_ptr()->get_scanner_geometry() == "Generic")
782 {
783 for (int axial_pos_num = do_symmetry_shift_z ? min_axial_pos_num : b.axial_pos_num();
784 axial_pos_num <= (do_symmetry_shift_z ? max_axial_pos_num : b.axial_pos_num());
785 ++axial_pos_num)
786 {
787 if (b.tangential_pos_num() >= min_tangential_pos_num && b.tangential_pos_num() <= max_tangential_pos_num)
788 {
789 Bin basic_bin(b);
790 find_basic_bin(basic_bin);
791 Bin bin_temp(b.segment_num(), b.view_num(), axial_pos_num, b.tangential_pos_num());
792 find_basic_bin(bin_temp);
793 if (basic_bin == bin_temp)
794 ax_tang_poss.push_back(AxTangPosNumbers(axial_pos_num, b.tangential_pos_num()));
795 }
796 }
797 }
798}
799
800void
802 const ViewSegmentNumbers& vs) const
803{
804#ifndef NDEBUG
805 {
806 ViewSegmentNumbers vstest = vs;
807 assert(find_basic_view_segment_numbers(vstest) == false);
808 }
809#endif
810
811 const int segment_num = vs.segment_num();
812 const int view_num = vs.view_num();
813
814 const bool symz = do_symmetry_swap_segment && (segment_num != 0);
815
816 rel_vs.reserve(num_related_view_segment_numbers(vs));
817 rel_vs.resize(0);
818
819 rel_vs.push_back(ViewSegmentNumbers(view_num, segment_num));
820
821 if (symz)
822 rel_vs.push_back(ViewSegmentNumbers(view_num, -segment_num));
823
824 if (do_symmetry_180degrees_min_phi && do_symmetry_90degrees_min_phi && (view_num % (num_views / 2)) != num_views / 4)
825 {
826 const int related_view_num = view_num < num_views / 2 ? view_num + num_views / 2 : view_num - num_views / 2;
827 rel_vs.push_back(ViewSegmentNumbers(related_view_num, segment_num));
828 if (symz)
829 rel_vs.push_back(ViewSegmentNumbers(related_view_num, -segment_num));
830 }
831
832 if (do_symmetry_180degrees_min_phi && (view_num % (num_views / 2)) != 0)
833 {
834 rel_vs.push_back(ViewSegmentNumbers(num_views - view_num, segment_num));
835 if (symz)
836 rel_vs.push_back(ViewSegmentNumbers(num_views - view_num, -segment_num));
837 }
838 if (do_symmetry_90degrees_min_phi && (view_num % (num_views / 4)) != 0)
839 {
840 // use trick to get related_view_num between 0 and num_views:
841 // use modulo num_views (but add num_views first to ensure positivity)
842 const int related_view_num = (num_views / 2 - view_num + num_views) % num_views;
843 rel_vs.push_back(ViewSegmentNumbers(related_view_num, segment_num));
844 if (symz)
845 rel_vs.push_back(ViewSegmentNumbers(related_view_num, -segment_num));
846 }
847
848 assert(rel_vs.size() == static_cast<unsigned>(num_related_view_segment_numbers(vs)));
849}
850
851END_NAMESPACE_STIR
Declaration of class stir::ProjDataInfoBlocksOnCylindrical.
Declaration of all symmetry classes for PET (cylindrical) scanners and cartesian images.
A class for storing coordinates and value of a single projection bin.
Definition Bin.h:49
int tangential_pos_num() const
get tangential position number
Definition Bin.inl:76
int axial_pos_num() const
get axial position number
Definition Bin.inl:70
void get_related_bins_factorised(std::vector< AxTangPosNumbers > &, const Bin &b, const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num) const override
fills in a vector with the axial and tangential position numbers related to this bin
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:740
float get_num_planes_per_axial_pos(const int segment_num) const
find correspondence between axial_pos_num and image coordinates
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:49
float get_num_planes_per_scanner_ring() const
find out how many image planes there are for every scanner ring
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:55
void get_related_view_segment_numbers(std::vector< ViewSegmentNumbers > &rel_vs, const ViewSegmentNumbers &vs) const override
fills in a vector with all the view/segments that are related to 'v_s' (including itself)
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:801
int num_related_view_segment_numbers(const ViewSegmentNumbers &vs) const override
returns the number of view_segment_numbers related to 'v_s'
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:677
bool find_basic_view_segment_numbers(ViewSegmentNumbers &v_s) const override
given an arbitrary view/segment, find the basic view/segment
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:399
unique_ptr< SymmetryOperation > find_symmetry_operation_from_basic_bin(Bin &) const override
given an arbitrary bin 'b', find the basic bin and the corresponding symmetry operation
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:666
bool find_basic_bin(Bin &b) const override
given an arbitrary bin 'b', find the basic bin
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:659
int num_related_bins(const Bin &b) const override
returns the number of bins related to 'b'
Definition DataSymmetriesForBins_PET_CartesianGrid.inl:688
const shared_ptr< const ProjDataInfo > proj_data_info_ptr
Member storing the info needed by get_related_bins() et al.
Definition DataSymmetriesForBins.h:163
void get_ring_pair_for_segment_axial_pos_num(int &ring1, int &ring2, const int segment_num, const int axial_pos_num) const
Find a ring pair that contributes to a segment and axial position.
Definition ProjDataInfoCylindrical.cxx:336
int segment_num() const
get segment number for const objects
Definition SegmentIndices.inl:32
int timing_pos_num() const
get TOF index for const objects
Definition SegmentIndices.inl:44
alias for ViewgramIndices
Definition ViewSegmentNumbers.h:34
int view_num() const
get view number for const objects
Definition ViewgramIndices.inl:36
Declaration of stir::error()
void warning(const char *const s,...)
Print warning with format string a la printf.
Definition warning.cxx:41
Coordinate2D< int > AxTangPosNumbers
AxTangPosNumbers as a class that provides the 2 remaining coordinates for a Bin, aside from ViewSegme...
Definition DataSymmetriesForBins.h:52
ProjDataInfoGenericNoArcCorr ProjDataInfoGeneric
For backwards compatibility.
Definition ProjDataInfoGenericNoArcCorr.h:175
Declaration of stir::warning()