Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent Affine Matrix Among Different Way of Converting #33

Open
cecilyen opened this issue Jan 25, 2018 · 17 comments
Open

Inconsistent Affine Matrix Among Different Way of Converting #33

cecilyen opened this issue Jan 25, 2018 · 17 comments

Comments

@cecilyen
Copy link

We are trying to build a working flow of our fMRI processing. The first step is converting Bruker's data to NIFTI. There are several ways of converting including using your bruker2nifti. But, I found the affine transformation is different among these methods. Below is the original parameters from Bruker, Bru2Nii, bruker2nifti and Bruker's DICOM+AFNI's to3d. Any ideas? PS. I am cross-posting this in Bru2Nii as well.

ParaVision 6.0.1
3D FLASH
##$PVM_Fov=( 3 )
58 58 58
##$PVM_SpatResol=( 3 )
0.453125 0.453125 0.453125
##$PVM_SPackArrSliceOrient=( 1 )
coronal
##$PVM_SPackArrReadOrient=( 1 )
H_F
##$PVM_SPackArrReadOffset=( 1 )
0
##$PVM_SPackArrPhase1Offset=( 1 )
0
##$PVM_SPackArrPhase2Offset=( 1 )
0
##$PVM_SPackArrSliceOffset=( 1 )
0
##$PVM_SPackArrGradOrient=( 1, 3, 3 )
0.241395201113481 0.0866782944696308 0.966548100276038
0.970351084097186 -0.00911025585436366 -0.24152800423488
-0.0121297349846693 0.996194698091746 -0.0863075490504608
##$VisuCoreOrientation=( 1, 9 )
0.970351084097186 -0.00911025585436366 0.24152800423488
0.241395201113481 0.0866782944696308 -0.966548100276038
-0.0121297349846693 0.996194698091746 0.0863075490504608

neurolabusc/Bru2Nii v1.0.20170719
qform_code 252 1 0
sform_code 254 1 1
quatern_b 256 1 0.0
quatern_c 260 1 0.0
quatern_d 264 1 0.0
qoffset_x 268 1 0.0
qoffset_y 272 1 0.0
qoffset_z 276 1 0.0
srow_x 280 4 0.43969 0.109382 0.005496 -35.769691
srow_y 296 4 0.109442 -0.437967 -0.039108 23.712318
srow_z 312 4 0.004128 -0.039276 0.451401 -26.848299

SebastianoF/bruker2nifti Sep 4, 2017 Python 2.7
qform_code 252 1 2
sform_code 254 1 1
quatern_b 256 1 -0.000757
quatern_c 260 1 -0.043614
quatern_d 264 1 -0.991602
qoffset_x 268 1 28.997601
qoffset_y 272 1 28.999701
qoffset_z 276 1 28.7724
srow_x 280 4 -0.439667 0.10943 -0.004123 28.997601
srow_y 296 4 -0.109384 -0.437991 0.039286 28.999701
srow_z 312 4 0.005483 0.039105 0.451403 28.7724

Converting Bruker DICOM to NIFTI by AFNI's to3d
qform_code 252 1 1
sform_code 254 1 1
quatern_b 256 1 0.086631
quatern_c 260 1 -0.670329
quatern_d 264 1 0.732007
qoffset_x 268 1 34.79163
qoffset_y 272 1 -28.773438
qoffset_z 276 1 18.542217
srow_x 280 4 -0.43969 -0.109382 0.005496 34.79163
srow_y 296 4 0.004128 -0.039276 -0.451401 30.91342
srow_z 312 4 0.109442 -0.437967 0.039108 18.542217

@SebastianoF
Copy link
Owner

SebastianoF commented Jan 25, 2018

Interesting comparison!

I had to discard the idea of passing through DICOM converted by Bruker because I found some inconsistencies (and in general converting twice may cause twice amount of problems).

An attempt to explain how I implemented the code can be found in the wiki page.

After diagonalising your outcomes, answers seem to confirm some coherence, at least in the scale if not in orientation and translation, between bru2nii and bruker2nifti:

In [2]: import numpy as np
In [3]: %paste

bru2nii = np.array([
[0.43969, 0.109382, 0.005496, -35.769691],
[0.109442, -0.437967, -0.039108, 23.712318],
[0.004128, -0.039276, 0.451401, -26.848299],
[0, 0, 0, 1]])


bruker2nifti = np.array([
[-0.439667, 0.10943, -0.004123, 28.997601],
[-0.109384, -0.437991, 0.039286, 28.999701],
[0.005483, 0.039105, 0.451403, 28.7724],
[0, 0, 0, 1]])

afni = np.array([
[-0.43969, -0.109382, 0.005496, 34.79163],
[0.004128, -0.039276, -0.451401, 30.91342],
[0.109442, -0.437967, 0.039108, 18.542217],
[0, 0, 0, 1]])

## -- End pasted text --

In [4]: np.diag(afni)
Out[4]: array([-0.43969 , -0.039276,  0.039108,  1.      ])

In [5]: np.diag(bru2nii)
Out[5]: array([ 0.43969 , -0.437967,  0.451401,  1.      ])

In [6]: np.diag(bruker2nifti)
Out[6]: array([-0.439667, -0.437991,  0.451403,  1.      ])

After some tests performed with the dataset benchmarking kindly offered by Michael Naveau bruker2nifti turned out to have an issue (the still open issue #11), that comes out only for some scanner settings.

Issue that did not affected my project, reason why I sill need to work out a way for solving this - without using the RECO parameter file, but only the VisuPars, as suggested in the manuals.

Please be patient, will try to investigate where is the flaw. I suspect it is in the sign of the stack progression in the VisuPars. In the meantime can you please copy and paste your VisuPars parameter file below? This would be very helpful!

@naveau
Copy link

naveau commented Jan 25, 2018

Thank you @SebastianoF for doing these tests. Do you have any idea on which scanner settings cause the issue ?
Isn't it related to the animal position declared at the creation of the study (HEAD_PRONE, HEAD_SUPINE)? I know that some of our users cheat the paravision software to get their images in neurological orientation like you wrote in your wiki.
Please let me know if you need some other dataset to test it.

(for cross reference, related to : neurolabusc/Bru2Nii#17)

@cecilyen
Copy link
Author

Does that mean Bru2Nii produce the right affine matrix? I am so confused.

Here are the visu_pars. I anonymize it.

##TITLE=Parameter List, ParaVision 6.0.1
##JCAMPDX=4.24
##DATATYPE=Parameter Values
##ORIGIN=Bruker BioSpin MRI GmbH
##OWNER=nmr
$$ 2018-01-09 16:12:02.735 -0500  nmr@...
$$ /opt/PV6.0.1/data/nmr/.../75/pdata/1/visu_pars
$$ process /opt/PV6.0.1/prog/bin/parxserver
##$VisuVersion=3
##$VisuUid=( 65 )
<...>
##$VisuCreator=( 65 )
<ParaVision>
##$VisuCreatorVersion=( 65 )
<6.0.1>
##$VisuCreationDate=<2018-01-09T16:12:02,733-0500>
##$VisuInstanceModality=( 65 )
<MR>
$$ @vis= VisuVersion VisuUid VisuCreator VisuCreatorVersion VisuCreationDate
##$VisuCoreFrameCount=1
##$VisuCoreDim=3
##$VisuCoreSize=( 3 )
128 128 128
$$ @vis= VisuInstanceModality VisuInstance VisuCoreFrameCount VisuCoreDim
##$VisuCoreDimDesc=( 3 )
spatial spatial spatial
##$VisuCoreExtent=( 3 )
58 58 58
##$VisuCoreFrameThickness=( 1 )
58
##$VisuCoreUnits=( 3, 65 )
<mm> <mm> <mm>
$$ @vis= VisuCoreSize VisuCoreDimDesc VisuCoreExtent VisuCoreFrameThickness
##$VisuCoreOrientation=( 1, 9 )
0.970351084097186 -0.00911025585436366 0.24152800423488 0.241395201113481 
0.0866782944696308 -0.966548100276038 -0.0121297349846693 0.996194698091746 
0.0863075490504608
##$VisuCorePosition=( 1, 3 )
-34.7916280996364 -30.913419003217 18.542217916812
##$VisuCoreDataMin=( 1 )
49.0679919525938
##$VisuCoreDataMax=( 1 )
32766.5
$$ @vis= VisuCoreUnits VisuCoreOrientation VisuCorePosition VisuCoreDataMin
##$VisuCoreDataOffs=( 1 )
0
##$VisuCoreDataSlope=( 1 )
0.00131442800501267
##$VisuCoreFrameType=( 1 )
MAGNITUDE_IMAGE
##$VisuCoreSlicePacksDef=(-1, 1)
$$ @vis= VisuCoreDataMax VisuCoreDataOffs VisuCoreDataSlope VisuCoreFrameType
##$VisuCoreSlicePacksSlices=( 1 )
(0, 1)
##$VisuCoreSlicePacksSliceDist=( 1 )
58
$$ @vis= VisuCoreSlicePacksDef VisuCoreSlicePacksSlices
##$VisuCoreWordType=_16BIT_SGN_INT
##$VisuCoreByteOrder=littleEndian
$$ @vis= VisuCoreSlicePacksSliceDist VisuCore VisuCoreWordType
##$VisuCoreDiskSliceOrder=disk_reverse_slice_order
$$ @vis= VisuCoreByteOrder VisuCoreDiskSliceOrder VisuPixel
##$VisuSubjectName=( 65 )
<Lime>
##$VisuSubjectId=( 65 )
<Lime>
##$VisuSubjectUid=( 65 )
<2.16.756.5.5.100.1889867029.31805.1494341627.7>
##$VisuSubjectInstanceCreationDate=(1494341627, 361, -240)
$$ @vis= VisuFrameOrderDesc VisuSubjectName VisuSubjectId VisuSubjectUid
##$VisuSubjectBirthDate=( 9 )
<...>
##$VisuSubjectSex=MALE
##$VisuSubjectType=Biped
$$ @vis= VisuSubjectInstanceCreationDate VisuSubjectBirthDate VisuSubjectSex
##$VisuStudyUid=( 65 )
<...>
##$VisuStudyDate=<2018-01-09T12:43:19,075-0500>
##$VisuStudyId=( 65 )
<...>
##$VisuStudyNumber=13
$$ @vis= VisuSubjectType VisuSubject VisuStudyUid VisuStudyDate VisuStudyId
##$VisuSubjectWeight=0.48
##$VisuStudyReferringPhysician=( 65 )
<nmr>
##$VisuStudyDescription=( 2048 )
<\
>
$$ @vis= VisuStudyNumber VisuSubjectWeight VisuStudyReferringPhysician
##$VisuExperimentNumber=75
##$VisuProcessingNumber=1
$$ @vis= VisuStudyDescription VisuStudy VisuExperimentNumber
##$VisuSeriesDate=<2018-01-09T16:12:02,733-0500>
##$VisuSubjectPosition=Head_Prone
##$VisuSeriesTypeId=( 65 )
<ACQ_BRUKER_PVM>
$$ @vis= VisuProcessingNumber VisuSeriesDate VisuSubjectPosition
$$ @vis= VisuSeriesTypeId VisuSeries VisuCoilReceive VisuCoilTransmit
##$VisuManufacturer=( 65 )
<Bruker BioSpin MRI GmbH>
##$VisuAcqSoftwareVersion=( 65 )
<6.0.1>
##$VisuInstitution=( 65 )
<...>
$$ @vis= VisuContrastAgent VisuManufacturer VisuAcqSoftwareVersion
##$VisuStation=( 65 )
<...>
##$VisuSystemOrderNumber=( 65 )
<...>
##$VisuAcqDate=<2018-01-09T16:10:59,720-0500>
$$ @vis= VisuInstitution VisuStation VisuSystemOrderNumber VisuEquipment
##$VisuAcqEchoTrainLength=1
##$VisuAcqSequenceName=( 40 )
<Bruker:FLASH>
##$VisuAcqNumberOfAverages=1
$$ @vis= VisuAcqDate VisuAcqEchoTrainLength VisuAcqSequenceName
##$VisuAcqImagingFrequency=300.347057651116
##$VisuAcqImagedNucleus=( 8 )
<1H>
##$VisuAcqRepetitionTime=( 1 )
3.3
$$ @vis= VisuAcqNumberOfAverages VisuAcqImagingFrequency VisuAcqImagedNucleus
##$VisuAcqPhaseEncSteps=128
##$VisuAcqPixelBandwidth=770.970394736842
##$VisuAcqFlipAngle=3.43984221342216
$$ @vis= VisuAcqRepetitionTime VisuAcqPhaseEncSteps VisuAcqPixelBandwidth
##$VisuAcqSize=( 3 )
128 128 128
##$VisuAcqImageSizeAccellerated=No
##$VisuAcqGradEncoding=( 3 )
phase_enc read_enc slice_enc
$$ @vis= VisuAcqFlipAngle VisuAcqSize VisuAcqImageSizeAccellerated
##$VisuAcqEchoTime=( 1 )
1.25
##$VisuAcquisitionProtocol=( 65 )
<1_AutoAlign_CorHF_128x128x128_99k_TE1p3_TRp033>
##$VisuAcqScanTime=54067.2
$$ @vis= VisuAcqGradEncoding VisuAcqEchoTime VisuAcquisitionProtocol
##$VisuAcqAntiAlias=( 3 )
1 1 1
##$VisuAcqEchoSequenceType=GradientEcho
##$VisuAcqSpinsVelocityEncoded=No
$$ @vis= VisuAcqScanTime VisuAcqAntiAlias VisuAcqEchoSequenceType
##$VisuAcqHasTimeOfFlightContrast=No
##$VisuAcqIsEpiSequence=No
$$ @vis= VisuAcqSpinsVelocityEncoded VisuAcqHasTimeOfFlightContrast
##$VisuAcqSpectralSuppression=NoSuppression
##$VisuAcqKSpaceTraversal=RectilinearTraversal
$$ @vis= VisuAcqIsEpiSequence VisuAcqSpectralSuppression
##$VisuAcqEncodingOrder=( 3 )
LinearEncoding LinearEncoding LinearEncoding
##$VisuAcqKSpaceTrajectoryCnt=1
$$ @vis= VisuAcqKSpaceTraversal VisuAcqEncodingOrder
##$VisuAcqKSpaceFiltering=( 3 )
NO_WDW NO_WDW NO_WDW
##$VisuAcqFlowCompensation=FlowNone
$$ @vis= VisuAcqKSpaceTrajectoryCnt VisuAcqKSpaceFiltering
##$VisuAcqSpoiling=RFAndGradientSpoiled
##$VisuAcqPartialFourier=( 3 )
1 1 1
##$VisuAcqParallelReductionFactor=( 3 )
1 1 1
$$ @vis= VisuAcqFlowCompensation VisuAcqSpoiling VisuAcqPartialFourier
##$VisuAcqMagnetizationTransfer=No
##$VisuAcqBloodSignalNulling=No
$$ @vis= VisuAcqParallelReductionFactor VisuAcqMagnetizationTransfer
##$VisuCardiacSynchUsed=No
##$VisuRespSynchUsed=No
$$ @vis= VisuAcqBloodSignalNulling VisuCardiacSynchUsed VisuRespSynchUsed
$$ @vis= VisuAcquisition VisuMpi VisuDepricated Visu
##END=
$$ File finished by PARX at 2018-01-09 16:12:02.735 -0500

@cecilyen
Copy link
Author

@naveau The position is HEAD_PRONE. The species is PRIMATE.

I also tested Matlab code from CristinaChavarrias/Bruker2nifti
qform_code 252 1 2
sform_code 254 1 2
quatern_b 256 1 0.670329
quatern_c 260 1 0.086631
quatern_d 264 1 -0.085554
qoffset_x 268 1 -34.517094
qoffset_y 272 1 -30.895844
qoffset_z 276 1 -18.377954
srow_x 280 4 0.43969 0.109382 -0.005496 -34.517094
srow_y 296 4 -0.004128 0.039276 0.451401 -30.895845
srow_z 312 4 -0.109442 0.437967 -0.039108 -18.377956

I am surprised that none of the conversion methods gives the same affine matrix. Any other way to convert Bruker data to NIFTI? I can't get francopestilli/mpi to work on my Linux machine.

@naveau
Copy link

naveau commented Jan 25, 2018

Did you try http://pvconv.sourceforge.net/ ?

@SebastianoF
Copy link
Owner

SebastianoF commented Jan 25, 2018

@cecilyen It is not too surprising. All converters are created with equal intentions, but some are more equals than others :-) .

As long as Bruker will not release an official converter, we can only guess what there is in the black box from input and output. Moreover, there is a huge difference between the requirements of the scanner and what you need in the nifti header.

For what I can tell from here, bru2nii works better than bruker2nifti. I think the latest version uses the RECO parameter file, that is not always available. But if you have them you should go for this one. Thanks for sharing the data will be helpful!

@naveau The typical VisuCoreOrientation that I had so far, for example for a 2D PILOT FOV sequence with three orthogonal volumes, is as follows:

##$VisuCoreOrientation=( 15, 9 )
1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 
0 1 0 0 0 1 0 1 0 0 0 -1 -1 0 0 0 1 0 0 0 -1 -1 0 0 0 1 0 0 0 -1 -1 0 0 0 1 0 
0 0 -1 -1 0 0 0 1 0 0 0 -1 -1 0 0 1 0 0 0 0 -1 0 1 0 1 0 0 0 0 -1 0 1 0 1 0 0 
0 0 -1 0 1 0 1 0 0 0 0 -1 0 1 0 1 0 0 0 0 -1 0 1 0
...
##$VisuCorePosition=( 15, 3 )
-12.8 -12.8 -6 -12.8 -12.8 -3 -12.8 -12.8 0 -12.8 -12.8 3 -12.8 -12.8 6 3 
-12.8 12.8 1.5 -12.8 12.8 0 -12.8 12.8 -1.5 -12.8 12.8 -3 -12.8 12.8 -12.8 3 
12.8 -12.8 1.5 12.8 -12.8 0 12.8 -12.8 -1.5 12.8 -12.8 -3 12.8

The one posted by @cecilyen is a 3D sequence, therefore there is only one orientation matrix.

My guess is that the orientation matrix represents a perpendicular vector to a plane of the stack of images. The intended moves would be understanding which one is the plane and where is located the origin. Then understanding if the 2D and 3D shares the same convention.

What do you think?

Thanks for the link! Now that I remember, I did not manage to make pvconv.pl run a a first attempt. This, plus the fact that perl scares me, I did not try to run it again.

@cecilyen
Copy link
Author

@naveau pvconv needs d3proc, which is obsoleted in PV 6.

I found lamyj/dicomifier can convert from Bruker to DICOM and then to NIFTI.
But, I can't compile it on my Linux or macOS boxes.

@cecilyen
Copy link
Author

cecilyen commented Jan 30, 2018

I did some reverse engineering on ParaVision 6's orientation.
First, there are four types of subject: Rodent, Primate, Material, and Unknown.
The material has no position different. Unknown is equal to Primate.
For Rodent and Primate, each can go Head or Feet first as well as Prone and Supine.
Each case corresponds to one Position_Matrix. For Primate Head Prone (the following discussion is based on this subject and position), the position matrix is
##[XX YY ZZ]
R=[+1 00 00]
P=[00 -1 00]
S=[00 00 -1]

Where R, P, and S are the readout, phase encoding and slice selection axis. XX, YY, and ZZ are X, Y, Z gradient axis. One can find the position matrix by setting angles of R, P, and S to zero in the PV 6. Then, find PVM_SPackArrGradOrient in the method file.

I will talk about PVM_SPackArrGradOrient in the next post.

@cecilyen
Copy link
Author

cecilyen commented Jan 30, 2018

PVM_SPackArrGradOrient is very tricky and is depending on the orientation of the subject. There are six orientations for Primate: Axial LR, Axial AP, Sagittal HF, Saggital AP, Coronal HF and Coronal LR In PV 6.0.1 manual p.660, Bruker mentioned about how to get gradient coordinate by ACQ_grad_matrix. ACQ_grad_matrix in acqp (input to the console) file is equal to PVM_SPackArrGradOrient in method file (input from the user). So, I substituted ACQ_grad_matrix by PVM_SPackArrGradOrient from now.
To convert from RPS (imaging coordinate) to XYZ (gradient coordinate), here is the equation to use:
(XX,YY,ZZ) = (R,P,S) * PVM_SPackArrGradOrient * Position_Matrix

Another parameter changed during geometry manipulation is PVM_SliceGeo. It is a new undocumented parameter only exists in PV 6. It is related to PVM_SPackArrGradOrient * Position_Matrix with R and P transposed depending on orientation.

Ps. I did all tests using Bruker's FLASH 3D sequence. I hope these findings are not changed by different sequences.

This is not the end of the story, I will cover how to get VisuCoreOrientation in the next post.

@cecilyen
Copy link
Author

cecilyen commented Jan 30, 2018

VisuCoreOrientation in visu_pars only exists for acquired and reconstructed data. It is affected by reconstruction parameters in reco file. One of the most important parameters is RECO_transposition, which is orientation dependent. If RECO_transposition is 1, the first two rows of the transformation matrix, i.e. R and P, is transposed. So does the final data. In this scenario, the final equation looks like:
(P,R,S) = VisuCoreOrientation * (YY,XX,ZZ)

I will list RECO_transposition, PVM_SPackArrGradOrien, and PVM_SliceGeo for six orientations in Primate Head Prone position in the following post.

@cecilyen
Copy link
Author

cecilyen commented Jan 30, 2018

The screenshots below are results from setting the orientation in ParaVision and then check the header file. Again, this only applies for Primate Head Prone position. I still couldn't figure out how Bruker convert Read, Phase, Slice angles to the transfomration matrix yet. Read, Phase, Slice are colored by red, green and blue following Bruker's convention.
screen shot 2018-01-30 at 10 59 30 am
screen shot 2018-01-30 at 10 59 51 am
screen shot 2018-01-30 at 11 02 30 am

@SebastianoF
Copy link
Owner

Very-interesting! Many thanks for sharing!
A couple of questions:

  • VisuSubjectPosition has the attribute {a}_{b} for a in {Head, Feet} and b in {Prone Supine}. Where do I find the animal category, as Rodent or Primate in the VisuPArs?
  • According to PV manuals, all that we need for the conversion should be found into the VisuPars. I welcomed this suggestion also in consequence of trickyness of the PVM_SPackArrGradOrient.
    Now you are confirming that the final (DICOM-like as RPS and not RAS) is encoded in the VisuCoreOrientation and VisuCorePosition. Do you think that the outcome of your investigation be boiled down to these parameters, or there is a reason why we should keep on considering the RECO parameter file?

Passages matrix between the PVM_SPackArrGradOrient and PVM_SliceGeo is

P = [1, 0, 0; 0, -1, 0; 0, 0, -1]

as P = inv(PVM_SPackArrGradOrient) * PVM_SliceGeo. For the passage between the rotation in degrees and the rotation matrix, the order in the vector counts, and it is the sequence of rotation corresponding to the transformation matrix, starting with the Anterior and Posterior inverted and the Superior Inferior inverted. E.g. (0,0,0) order=RPS is exactly the passage matrix above.

Not sure the connection between the abbreviation AxLR, AxAP, SagHF... The only thing I can think of is that HF stands for HeadFoot, but can not say if for primates or rodents as it is not the same.

@cecilyen
Copy link
Author

cecilyen commented Jan 30, 2018

@SebastianoF

  • Subject type can be found in subject file or visu_pars as SUBJECT_type or VisuSubjectType, respectively. Three types are defined: Biped (Primate, Unknown), Quadruped (Rodent) and Other (Material)
  • Thanks for mentioning the manual. I may be wrong about the visu* parameters here. I will update my post as I read through the manual. It's in D02_PvParams p.65, right?
    The reason I am trying to figure out the whole orientation mess is that I am trying to implement AutoAlign from Siemens. The idea is to align the acquisition orientation to a reference image. So, I need to find out how Bruker change their gradient orientation from the geometry.
    I might not state clearly. RPS here represents Read, Phase, and Slice, which is orientation dependent. It is NOT Right, Posterior, and Superior as defined in DICOM. They confused me as well.
  • Primate and rodent (in parathesis) coordinates
    Right->Left(Same), Anterior->Posterior (Ventral->Dorsal), Foot->Head (Caudal->Rostral)

@cecilyen
Copy link
Author

cecilyen commented Feb 2, 2018

For Primate Head Prone, ParaVision is using Neurological RAS coordinate, i.e. +X, +Y and +Z is on the Right, Anterior and Superior/Head.

@SebastianoF
Copy link
Owner

SebastianoF commented Feb 6, 2018

@cecilyen: Thanks for your answers! You may be interested to know that I recently received a scanned sample in raw data with the same VisuPars parameter file of another study. Both are 3D scans.
After conversion with bruker2nifti (Lo and Alas!) one was properly converted, the other one resulted flipped upside down. They have the same information in the VisuPars, but probably not the same FOV was selected in the acquisition.
After some Yogic breathing, I am trying to understand a bit more of what is going on, and may have soon some insight. Until then, for the moment it seems to me that there are crucial scanner settings for the conversion that are not stored into the VisuPars parameter file. So, or I find the missing input somewhere else (maybe in the RECO - that is not always provided), or an ideal converter may not be realisable in practice, and tuning will always be necessarily.
(Having said that... still hoping that one day ParaVision will provide the option export to nifti).

@SebastianoF
Copy link
Owner

SebastianoF commented Feb 6, 2018

Would like to share a small experiment I did to be sure about what I was saying in the previous post.

Let A and B be two studies consecutively acquired in the same place, with the same protocol, where

  • subject A, scan 1 is correctly converted
  • subject B, scan 1 is incorrectly converted (upside down)

Now, if I switch the VisuPars parameter files between the two scans and I do the conversion again, I got exactly the same result: A is still correctly converted reading the VisuPars of B, and B is still incorrectly converted reading the VisuPars of A.

Option 1: there is another file encoding the missing information to realign correctly the image.
Option 2: the missing information is not encoded anywhere and the image is irreversibly stored upside down in the 2dseq.

If option 1 is correct, the @naveau converter based also on RECO is more insightful. Although RECO is not always available.

In both cases, there is a counterxample disproving that VisuPars have all the information to reconstruct the affine matrix. This may also explain why the DICOM created by ParaVision are raising as well controversies: according to the manual, in passing from the raw data to DICOM, only the VisuPars is considered.

Will let you know more in the next episode about what exactly was different from one scan to the other during the acquisition (there was certainly something different) as soon as I will be talking with the radiologists.

In the meantime this one and related issues will remain open...

@gdevenyi
Copy link
Contributor

An addendum to @cecilyen, for PV5.1, there is no VisuSubjectType in visu_pars, furthermore, "Human" is an option for SUBJECT_type in subject.

I don't see a mention of the options in the manual, I will have to dig through at the console next time I can check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants