Skip to content

flexibility in SPECT data and associated difficulties for analytic algorithms #1635

@KrisThielemans

Description

@KrisThielemans

Originally posted by @Dimitra-Kyriakopoulou in #1631 (comment)_

In addition, I would like to express a few thoughts that came to my mind regarding the 90 degree rotation and z-flip issues, which can appear as the current left–right (x) problem:

A) src/IO/InterfilePDFSHeaderSPECT.cxx

num_segments = 1;
num_views = -1;
add_key("number of projections", &num_views);
start_angle = 0;
add_key("start angle", &start_angle);
direction_of_rotation = "cw";
add_key("direction of rotation", &direction_of_rotation);
extent_of_rotation = double_value_not_set;
add_key("extent of rotation", &extent_of_rotation);

Different datasets may use different 0° definitions and CW/CCW sense. That creates a global rotation (often ~90°) which can be misread as “left/right” mismatch.

Hence, right after the add_key(...) calls, we must normalize angles to one convention (0°=+x, CCW, positive increment). If already canonical, it’s a no-op; otherwise it corrects once and removes rotation ambiguity.

B) src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx

vol.Xcmd2 = vol.Ncold2 * vol.szcm; // half-size x (cm)
vol.Ycmd2 = vol.Nrowd2 * vol.szcm; // half-size y (cm)
vol.Zcmd2 = vol.Nslid2 * vol.thcm; // half-size z (cm)

vol.x0 = ((float)0.5 - vol.Ncold2) * vol.szcm; // x of first voxel
vol.y0 = ((float)0.5 - vol.Nrowd2) * vol.szcm; // y of first voxel
vol.z0 = ((float)0.5 - vol.Nslid2) * vol.thcm; // z of first voxel

Some images arrive with opposite z slice order . That appears as a z-mirror during comparison and can confuse left/right assessments.

At image load (or just before the projector binds the VoxelsOnCartesianGrid), we can add a one-time z-order normalization that reorders slices to canonical +z.

C) src/recon_buildblock/SPECTUB_Tools.cxx

const float deg = wmh.prj.ang0 + (float)i * wmh.prj.incr; // angle (deg)
ang[i].cos = cos(deg * dg2rd);
ang[i].sin = sin(deg * dg2rd);

// reduced angle to 0..45°, track quadrant
float angR = fabs(deg);
int   quad = (int)floor(angR / 90.f);
angR       = fabs(angR - 90.f * quad);
if (angR > 45.f) angR = fabs(90.f - angR);

// detector-line direction and first-bin position
ang[i].incx  = wmh.prj.szcm * ang[i].cos;  // Δx per bin
ang[i].incy  = wmh.prj.szcm * ang[i].sin;  // Δy per bin

ang[i].xbin0 = -ang[i].Rrad * ang[i].sin - wmh.prj.lngcmd2 * ang[i].cos;
ang[i].ybin0 =  ang[i].Rrad * ang[i].cos - wmh.prj.lngcmd2 * ang[i].sin;

If (A) isn’t normalized, deg carries incompatible angle conventions, making the geometry rotated (which may be perceived as left/right mismatch).

We must ensure deg = ang0 + i*incr is exactly the canonical CCW, 0°=+x delivered by (A):

  • If A is in place → no code change usually needed in C.
  • If any extra CW/CCW or handedness reinterpretation exists here → remove it so C simply uses the canonical angles.

D) src/recon_buildblock/SPECTUB_Weight3d.cxx

float ux = bin.x - vox.x;
float uy = bin.y - vox.y;
float uz = bin.z - vox.z;

int signx = SIGN(ux);
int signy = SIGN(uy);
int signz = SIGN(uz);

// ... next-plane distances and stepping ...
dx = (next_x - vox.x) / (ux + EPSILON);
dy = (next_y - vox.y) / (uy + EPSILON);
dz = (next_z - vox.z) / (uz + EPSILON);

If z-order is normalized once at (B), D is automatically correct for all cases.

_Origin

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions