STIR 6.5.0
PETSIRDInfo_helpers.h
Go to the documentation of this file.
1/*
2 Copyright 2026, University Medical Center Groningen
3 Copyright 2025, MGH / HST A. Martinos Center for Biomedical Imaging
4
5 SPDX-License-Identifier: Apache-2.0
6 See STIR/LICENSE.txt for details.
7
8 */
9
18
19#ifndef __stir_IO_PETSIRDInfo_helpers_H__
20#define __stir_IO_PETSIRDInfo_helpers_H__
21
23#include <array>
24#include <set>
25#include "stir/format.h"
26#include "stir/info.h"
27#include <vector>
28
54
55namespace matrix
56{
57
58using Mat3 = std::array<std::array<float, 3>, 3>;
59using Vec3 = std::array<float, 3>;
60
66inline Mat3
67transpose(const Mat3& mat)
68{
69 std::array<std::array<float, 3>, 3> result{};
70 for (int i = 0; i < 3; ++i)
71 for (int j = 0; j < 3; ++j)
72 result[j][i] = mat[i][j];
73 return result;
74}
75
82inline Mat3
83subtract(const Mat3& A, const Mat3& B)
84{
85 std::array<std::array<float, 3>, 3> result{};
86 for (size_t i = 0; i < 3; ++i)
87 for (size_t j = 0; j < 3; ++j)
88 result[i][j] = A[i][j] - B[i][j];
89 return result;
90}
91
97inline Vec3
98getAxisFromSkew(const Mat3& S)
99{
100 return {
101 0.5f * (S[2][1] - S[1][2]), // x
102 0.5f * (S[0][2] - S[2][0]), // y
103 0.5f * (S[1][0] - S[0][1]) // z
104 };
105}
106
107} // namespace matrix
108
119namespace vector_utils
120{
131inline bool
132get_spacing_uniform(std::vector<float>& spacing, const std::set<float>& unsorted_block_poss, double epsilon = 1e-4)
133{
134 std::vector<float> sorted_z(unsorted_block_poss.begin(), unsorted_block_poss.end());
135 for (size_t i = 1; i < sorted_z.size(); ++i)
136 {
137 spacing.push_back(std::abs(sorted_z[i] - sorted_z[i - 1]));
138 }
139
140 return std::all_of(spacing.begin(), spacing.end(), [&](float s) { return std::abs(s - spacing.front()) <= epsilon; });
141}
142
153const std::set<float>&
154get_largest_vector(const std::set<float>& x, const std::set<float>& y, const std::set<float>& z)
155{
156 const std::set<float>* largest = &x;
157 if (y.size() > largest->size())
158 {
159 largest = &y;
160 }
161 if (z.size() > largest->size())
162 {
163 largest = &z;
164 }
165 return *largest;
166}
167
173void
174find_unique_values_1D(std::set<float>& values, const std::vector<float>& input)
175{
176 for (float val : input)
177 {
178 // std::cout << val << std::endl;
179 values.insert(val);
180 }
181}
182
188void
189find_unique_values_2D(std::set<float>& values, const std::vector<std::vector<float>>& input)
190{
191 for (size_t row = 0; row < input.size(); ++row)
192 for (size_t col = 0; col < input[row].size(); ++col)
193 values.insert(input[row][col]);
194}
195
196} // namespace vector_utils
197
198bool
199almostEqual(double a, double b, double tol = 1e-6)
200{
201 return std::fabs(a - b) <= tol;
202}
203
204// Detect groupSize along dim2 (y) and loops along dim3 (z)
205// Returns true on success, false if pattern doesn't match the assumed structure.
206bool
207inferGroupSizes_dim2_dim3(const std::vector<stir::CartesianCoordinate3D<float>>& pts,
208 std::size_t& groupSize_dim2,
209 std::size_t& groupSize_dim3,
210 float tol = 1e-5f)
211{
212 const std::size_t n = pts.size();
213 groupSize_dim2 = groupSize_dim3 = 0;
214
215 if (n == 0)
216 return false;
217
218 if (n == 1)
219 {
220 groupSize_dim2 = 1;
221 groupSize_dim3 = 1;
222 return true;
223 }
224
225 // STIR CartesianCoordinate3D<T> is (z, y, x)
226 const float x0 = pts[0].x();
227 const float z0 = pts[0].z();
228
229 // 1) Find how many initial points keep x and z the same
230 std::size_t runLen = 1;
231 while (runLen < n && almostEqual(pts[runLen].x(), x0, tol) && almostEqual(pts[runLen].z(), z0, tol))
232 {
233 ++runLen;
234 }
235
236 groupSize_dim2 = runLen;
237
238 // Must tile the full array
239 if (groupSize_dim2 == 0 || n % groupSize_dim2 != 0)
240 return false;
241
242 groupSize_dim3 = n / groupSize_dim2;
243
244 // 2) Check each block of size groupSize_dim2 has constant x,z
245 for (std::size_t b = 0; b < groupSize_dim3; ++b)
246 {
247 std::size_t start = b * groupSize_dim2;
248 float xb = pts[start].x();
249 float zb = pts[start].z();
250
251 for (std::size_t i = 1; i < groupSize_dim2; ++i)
252 {
253 const auto& p = pts[start + i];
254 if (!almostEqual(p.x(), xb, tol) || !almostEqual(p.z(), zb, tol))
255 {
256 return false; // pattern breaks inside a block
257 }
258 }
259 }
260
261 // 3) Optionally check that z actually changes between blocks
262 for (std::size_t b = 1; b < groupSize_dim3; ++b)
263 {
264 float z_prev = pts[(b - 1) * groupSize_dim2].z();
265 float z_curr = pts[b * groupSize_dim2].z();
266 if (almostEqual(z_prev, z_curr, tol))
267 {
268 return false; // outer loop didn't move in z
269 }
270 }
271
272 return true;
273}
274
275#endif
defines the stir::CartesianCoordinate3D<coordT> class
a templated class for 3-dimensional coordinates.
Definition CartesianCoordinate3D.h:53
Include for formatting function. Use standard library version if available (C++20 and newer)....
Declaration of stir::info().
Lightweight 3x3 matrix and 3D vector helpers used during PETSIRD geometry analysis.
Vec3 getAxisFromSkew(const Mat3 &S)
Extract rotation axis from the skew-symmetric matrix S = R - R^T.
Definition PETSIRDInfo_helpers.h:98
Mat3 subtract(const Mat3 &A, const Mat3 &B)
Subtract two 3x3 matrices (A - B).
Definition PETSIRDInfo_helpers.h:83
Mat3 transpose(const Mat3 &mat)
Transpose a 3x3 matrix.
Definition PETSIRDInfo_helpers.h:67
Helpers for spacing analysis and axis inference from coordinate sets.
const std::set< float > & get_largest_vector(const std::set< float > &x, const std::set< float > &y, const std::set< float > &z)
Return the largest of three sets and report inferred axial direction.
Definition PETSIRDInfo_helpers.h:154
void find_unique_values_2D(std::set< float > &values, const std::vector< std::vector< float > > &input)
Collect unique values from a 2D vector (matrix).
Definition PETSIRDInfo_helpers.h:189
bool get_spacing_uniform(std::vector< float > &spacing, const std::set< float > &unsorted_block_poss, double epsilon=1e-4)
Compute spacings between sorted unique values and test uniformity.
Definition PETSIRDInfo_helpers.h:132
void find_unique_values_1D(std::set< float > &values, const std::vector< float > &input)
Collect unique values from a 1D vector.
Definition PETSIRDInfo_helpers.h:174