Schema2dseq gained _apply_transposition, which transposes the XY axes of
every frame whose RECO_transposition / VisuCoreTransposition flag is set.
I believe the reconstruction has already applied that transposition when it
wrote the 2dseq, so applying it on read transposes the image. Two independent
lines of evidence, neither of which relies on any converter's output.
1. It contradicts the geometry parameters in the same file
Any flagged non-square reconstruction shows this. One I have to hand, PV6.0.1,
3D:
VisuCoreSize [178, 200, 100]
VisuCoreExtent [16.02, 18.00, 9.00]
The field of view divides by the matrix, in VisuCoreSize order, into isotropic
0.090 mm voxels — which is what the acquisition was. Pair that same extent with
the transposed array (200, 178, 100) and the voxels come out 0.080 x 0.101 mm.
So VisuCoreExtent describes the untransposed array, and by construction so do
VisuCorePosition and VisuCoreOrientation. Transposing data alone puts it at
odds with the whole geometry block a consumer uses to build an affine — and the
affine is unchanged by this code, so the image and its geometry now disagree.
2. Two acquisitions of the same object stop agreeing
I have an orientation phantom study containing pairs that differ only in
readout direction — the variable that flips this flag. Same phantom, same
session, same slice orientation, same voxel size, identical direction cosines.
Two images of the same object in the same plane must hold the same array.
Correlating each pair as-is, and with one member's in-plane axes swapped:
| pair |
|
as-is |
one transposed |
| PV6.0.1 coronal 2D |
flag left alone |
+0.951 |
+0.283 |
|
0.4, flag applied |
+0.283 |
+0.951 |
| PV6.0.1 axial 3D |
flag left alone |
+0.992 |
+0.532 |
|
0.4, flag applied |
+0.532 |
+0.992 |
| PV5.1 axial 3D |
flag left alone |
+0.928 |
+0.426 |
|
0.4, flag applied |
+0.426 |
+0.928 |
The pairs already agree when the flag is left alone. Applying it is what makes
two images of the same object disagree, and the prediction inverts exactly
between library versions.
Consistent with the ParaVision docs
ATB_SetRecoTranspositionFromLoops is documented as the method program
"calculating the values of the RECO_transposition array depending on the image
orientation" — the sequence telling the reconstruction what to do, which the
reconstruction then does. The parameter reads as a record, not an instruction.
Scope
The flag is set on 1,142 of 1,561 reconstructions (73%) in the corpus I test
against, spanning PV5.1, 6.0.1, 7.0.0 and PV360 — so this changes the pixel data
of most datasets, silently.
What I have not established
- Only PV5.1 and PV6.0.1 are covered by the pair experiment. Not 7.0.0 or
PV360 — and the flag's source differs (RECO_transposition for 1,052 of those
reconstructions, VisuCoreTransposition for 90), so they need not behave alike.
- All three pairs are axis-aligned; oblique acquisitions are untested.
- The phantom study's redistributability is unestablished, so I can't attach it.
The geometry argument in (1) needs no data from me — it reproduces on any
flagged non-square reconstruction you have.
Happy to be wrong about this if there's a dataset where the transposition is
genuinely pending on read; that would be very useful to see.
See also #(the shape issue), which is a separate defect in the same code path.
🤖 Generated with Claude Code
https://claude.ai/code/session_019P9Qp72Xymjic1eT9sMB5Q
Schema2dseqgained_apply_transposition, which transposes the XY axes ofevery frame whose
RECO_transposition/VisuCoreTranspositionflag is set.I believe the reconstruction has already applied that transposition when it
wrote the 2dseq, so applying it on read transposes the image. Two independent
lines of evidence, neither of which relies on any converter's output.
1. It contradicts the geometry parameters in the same file
Any flagged non-square reconstruction shows this. One I have to hand, PV6.0.1,
3D:
The field of view divides by the matrix, in
VisuCoreSizeorder, into isotropic0.090 mm voxels — which is what the acquisition was. Pair that same extent with
the transposed array
(200, 178, 100)and the voxels come out 0.080 x 0.101 mm.So
VisuCoreExtentdescribes the untransposed array, and by construction so doVisuCorePositionandVisuCoreOrientation. Transposingdataalone puts it atodds with the whole geometry block a consumer uses to build an affine — and the
affine is unchanged by this code, so the image and its geometry now disagree.
2. Two acquisitions of the same object stop agreeing
I have an orientation phantom study containing pairs that differ only in
readout direction — the variable that flips this flag. Same phantom, same
session, same slice orientation, same voxel size, identical direction cosines.
Two images of the same object in the same plane must hold the same array.
Correlating each pair as-is, and with one member's in-plane axes swapped:
The pairs already agree when the flag is left alone. Applying it is what makes
two images of the same object disagree, and the prediction inverts exactly
between library versions.
Consistent with the ParaVision docs
ATB_SetRecoTranspositionFromLoopsis documented as the method program"calculating the values of the
RECO_transpositionarray depending on the imageorientation" — the sequence telling the reconstruction what to do, which the
reconstruction then does. The parameter reads as a record, not an instruction.
Scope
The flag is set on 1,142 of 1,561 reconstructions (73%) in the corpus I test
against, spanning PV5.1, 6.0.1, 7.0.0 and PV360 — so this changes the pixel data
of most datasets, silently.
What I have not established
PV360 — and the flag's source differs (
RECO_transpositionfor 1,052 of thosereconstructions,
VisuCoreTranspositionfor 90), so they need not behave alike.The geometry argument in (1) needs no data from me — it reproduces on any
flagged non-square reconstruction you have.
Happy to be wrong about this if there's a dataset where the transposition is
genuinely pending on read; that would be very useful to see.
See also #(the shape issue), which is a separate defect in the same code path.
🤖 Generated with Claude Code
https://claude.ai/code/session_019P9Qp72Xymjic1eT9sMB5Q