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

Real and imaginary images in Philips Enhanced #189

Closed
baxpr opened this issue May 23, 2018 · 16 comments
Closed

Real and imaginary images in Philips Enhanced #189

baxpr opened this issue May 23, 2018 · 16 comments

Comments

@baxpr
Copy link

baxpr commented May 23, 2018

I have encountered a recent Philips DICOM with multiple image types and cardiac
phases where the scale slope and intercept vary from image to image. Here is the
file:
http://www.vuiis.vanderbilt.edu/~rogersbp/Smith_20180405.06.01.10-40-12.WIP_MP2RAGE_wip_TFEprepulsecardiac_Experiment1_SENSE.01.DCM

dcm2niix is not able to create Nifti images from this. Here is the output:

$ dcm2niix -v 1 Smith_20180405.06.01.10-40-12.WIP_MP2RAGE_wip_TFEprepulsecardiac_Experiment1_SENSE.01.DCM 
Compression will be faster with 'pigz' installed http://macappstore.org/pigz/
Chris Rorden's dcm2niiX version v1.0.20180518 Clang8.0.0 (64-bit MacOS)
Found 1 DICOM image(s)
dimensionIndexValues0020x9157[2] = [1 104 2 1 ]
dimensionIndexValues0020x9157[3] = [1 104 0 1 ]
dimensionIndexValues0020x9157[4] = [1 104 3 1 ]
dimensionIndexValues0020x9157[5] = [1 105 1 1 ]
dimensionIndexValues0020x9157[6] = [1 105 2 1 ]
dimensionIndexValues0020x9157[7] = [1 105 0 1 ]
dimensionIndexValues0020x9157[8] = [1 105 3 1 ]
dimensionIndexValues0020x9157[9] = [1 106 1 1 ]
dimensionIndexValues0020x9157[10] = [1 106 2 1 ]
dimensionIndexValues0020x9157[11] = [1 106 0 1 ]
dimensionIndexValues0020x9157[12] = [1 106 3 1 ]
dimensionIndexValues0020x9157[13] = [1 107 1 1 ]
dimensionIndexValues0020x9157[14] = [1 107 2 1 ]
dimensionIndexValues0020x9157[15] = [1 107 0 1 ]
dimensionIndexValues0020x9157[16] = [1 107 3 1 ]
dimensionIndexValues0020x9157[17] = [1 108 1 1 ]
dimensionIndexValues0020x9157[18] = [1 108 2 1 ]
DICOM file /Users/rogersbp/Dropbox (Personal)/nii_debug/phantom/Smith_20180405.06.01.10-40-12.WIP_MP2RAGE_wip_TFEprepulsecardiac_Experiment1_SENSE.01.DCM:
 patient position (0020,0032)	76.3187	-94.1538	91.7883
 patient position end (0020,0032)	-67.1369	-97.1293	89.8337
 orient (0020,0037)	-0.0228734	0.982907	0.182675	0.00960038	0.182931	-0.983079
 acq 6 img 1 ser 601 dim 512x512x206x8 mm 0.429688x0.429688x0.7 offset 3661050 loc 206 valid 1 ph 1 mag 1 nDTI 0 3d 1 bits 16 littleEndian 1 echo 1 coil 0 TE 2.662 TR 6
Parameters vary across 3D volumes packed in single DICOM file:
 0 TE=2.662 Slope=4.60317 Inter=-9425 PhilipsScale=0.00199889 Phase=0
 1 TE=2.662 Slope=1.53455 Inter=-3142 PhilipsScale=651.74 Phase=0
 2 TE=2.662 Slope=4.60317 Inter=-9425 PhilipsScale=0.00199889 Phase=0
 3 TE=2.662 Slope=4.60293 Inter=0 PhilipsScale=0.00199889 Phase=1
 4 TE=2.662 Slope=4.60317 Inter=-9425 PhilipsScale=0.00199889 Phase=0
 5 TE=2.662 Slope=1.53455 Inter=-3142 PhilipsScale=651.74 Phase=0
 6 TE=2.662 Slope=4.60317 Inter=-9425 PhilipsScale=0.00199889 Phase=0
 7 TE=2.662 Slope=4.60293 Inter=0 PhilipsScale=0.00199889 Phase=1
Error: No files saved: DEBUG_READ_NOT_WRITE set
No valid DICOM files were found

After some digging, my best guess is that the REAL and IMAGINARY types do not
trigger the routine that splits the output into multiple 3D Niftis instead of a
single 4D Nifti. This split is necessary here because slope and intercept vary
between images.

Here are the eight unique image types in the example DICOM. We should get one 3D
Nifti for each:

RWVSlope    RWVIntercept    p2005_100e    CplxImgComp    CardTrigDel
________    ____________    __________    ___________    ___________

1.5346      -3142              651.74     'PHASE'         812       
1.5346      -3142              651.74     'PHASE'        3012       
4.6029          0           0.0019989     'MAGNITUDE'     812       
4.6029          0           0.0019989     'MAGNITUDE'    3012       
4.6032      -9425           0.0019989     'IMAGINARY'     812       
4.6032      -9425           0.0019989     'IMAGINARY'    3012       
4.6032      -9425           0.0019989     'REAL'          812       
4.6032      -9425           0.0019989     'REAL'         3012      

RWVSlope is RealWorldValueSlope, (0040,9224)
RWVIntercept is RealWorldValueIntercept, (0040,9225)
p2005_100e is the Philips private scaling factor, (2005,100E)
CplxImgComp is ComplexImageComponent, (0008,9208)
CardTrigDel is CardiacTriggerDelayTime, (0020,9153)

@neurolabusc
Copy link
Collaborator

Please try the latest code - it should extract 4 NIfTI images each with two volumes: phase, magnitude, imaginary and real. I know that PAR/REC is being deprecated, but if possible can you export this sequence to PAR/REC? It should be easy to support PAR/REC if I know the values Philips uses for the real and imaginary components.

@baxpr
Copy link
Author

baxpr commented May 23, 2018

Nice! Thanks. I am getting the 4 Nifti files now.

Here are some PAR/RECs for that scan:
http://www.vuiis.vanderbilt.edu/~rogersbp/Smith_20180405.XMLPARREC.zip
Note though that these were reconstructed by us offline so are not quite what the scanner produces.

In the Niftis, though, the ranges of values are not what I'm expecting:

Magnitude: expected 0 to 2.3e5, observed -10.2e5 to -7.9e5
Phase: expected -3.14 to 3.14, observed 0 to 20.4e5
Real: expected approx -2e5 to 2e5, observed -0.60 to 0.60
Imaginary: expected approx -2e5 to 2e5, and got it - this one seems ok

I observed the same thing in a different DICOM of patient data with only real/imag/mag images: range of values look ok for imaginary, but not for magnitude and real images.

Also, how can we determine from the Nifti files which cardiac phase is associated with which volume? I don't see any related info in the Nifti header. The pixdim for the 4th dimension is showing up as 0.006 which doesn't have any obvious relationship to the two values that were used (812 and 3012).

@neurolabusc
Copy link
Collaborator

Can you provide the NIfTI images generated by Philips own direct NIfTI exporter. Philips is packing an awful lot of data (which is great), but this is becoming very complex to disentangle. It would be nice to see how their own tool handles this sequence.

@baxpr
Copy link
Author

baxpr commented May 24, 2018

The scanner will not generate Nifti images for this scan, as it turns out. However here is the scanner-generated PAR/REC in case that's useful: http://www.vuiis.vanderbilt.edu/~rogersbp/Smith_20180405_scanner_PARREC.tgz

@neurolabusc
Copy link
Collaborator

neurolabusc commented May 24, 2018

Your sample includes three different forms of image intensity scaling: the 'precise' floating point value, the display value and the "real world value". In the past, I have supported converting to either of the first two using "-p y"/"-p n" option as some groups prefer one convention or the other (see also PMC3998685).

Can you or someone else familiar with Philips data (e.g. @drmclem, @vuiiscci) tell me if RWV supersedes the other two and if available should always be used during DICOM to NIfTI conversion, or will different users still require different scaling factors? I can always store the various intensity scaling transforms in the BIDS header, but it would be nice to have some guidance on what is the simplest approach for most users.

@baxpr
Copy link
Author

baxpr commented May 24, 2018

What is the "real world value" that is different from PV, DV, and FP?

My understanding was that for this computation,

RS is RealWorldValueSlope (0040,9224)
RI is RealWorldValueIntercept (0040,9225)
SS is the Philips private scaling factor (2005,100E)

If I apply the standard Philips boilerplate to this PAR/REC file under that assumption, I obtain FP values that make sense to me and are in line with what I posted above - but are different from the values that the new modifications to dcm2niix produce from the DICOM.

For what it's worth, my current personal definition of "simplest for most users" is to store the slope and intercept that produce the FP values, which I believe was already your default approach. I.e. Nifti slope of 1/SS and intercept of RI/(RS * SS) if I did the math right.

@drmclem
Copy link

drmclem commented May 24, 2018

HI

Real world is found on scans where the actual data has units (so Phase, ms, Bo etc) and will consist of just a slope and intercept to map from DICOM ints to those units. For Philips that should be identical to the old RS and RI which were present on those scans. In the cases I have seen where this has shown up (and it is both a recent release feature on R5.1 and above, and depends on the "catching" software if sent over a DICOM node) it will be one or the other but the real world mapping block will also have a unit string to go with it. I have seen some cases where a scan has both present but I have been unable to confirm the transport conditions where this happen.

The other private scale factor took the data back a step further to the source floating point and to allow comparison of signals between sequential scans. This shouldn't be needed for RWV as they have units and are therefore comparable already.

for the phase images above you can see that the RWV units map 0-4096 to -PI1000 - +PI1000 (the the x1000 are there as most PACS are not happy displaying small floating point values). factor from 2005_200e not needed.

The other three image types have their own factors (Magnitude forced positive, real and imag signed) and for those you have a choice - if you are only comparing within scan you can use the factor or not as they will be all scaled the same within the can for a given type. But using the RS and RI values will make it more sensible (ie. the former will always be betwen 0-4096, the latter will look more intuitive for Real and Imag as it will convert it to signed).

@neurolabusc
Copy link
Collaborator

Please try out the latest update

  1. It will provide the real world intercept and scaling when provided (thanks for the description @drmclem).
  2. It includes the JSON BIDS tag TriggerDelayTime
  3. In the event that an image includes multiple TriggerDelayTimes, conversion will report it this during conversion, e.g. "TriggerDelayTime varies 3012 812". At some stage in the future, it might be nice to include the array of trigger delay times. The order of volumes is determined by Dimension Index Values (0020,9157), so I presume it is pretty predictable.
  4. The changes I have made provide better support for Philips images, but require stricter compliance to the Philips enhanced DICOM conventions. This means the conversion may be more sensitive to someone manipulating the DICOM image (e.g. anonymization or saving with an implicit VR). The Philips enhanced DICOMs have many benefits, but users need to be very vigilant when using legacy tools with this modern format.

@baxpr
Copy link
Author

baxpr commented May 24, 2018

Gotcha - I was a step behind then.

I just verified with the PAR/REC that using RS and RI produces appropriate results as far as I can tell. I also tested commit eca396d52706f on our brain data and it is working like I expect. In both cases our postprocessing is generating correct images instead of the nonsense we were getting previously.

So, as far as I can tell, this is working for me now.

You could potentially clarify in the usage text that -p chooses between RS/RI and Philips FP, if that is the case. I gather you're not offering Philips DV as an option going forward? Anyway I have never used it.

Thanks for the help!

@neurolabusc
Copy link
Collaborator

Glad this works. With the current release, the real world scale and slope are used if available. If they are not available, the users have a choice between the display value ("-p n") and floating point value ("-p y"). I know some groups prefer one to the other. In all cases, the other values are stored in the BIDS json file. Thanks for the sample dataset.

I realize you are busy, but I would appreciate it if you could test this version out - I do not have access to many Philips datasets and this was a pretty large change considering that I am eager to push out a new stable release.

@baxpr
Copy link
Author

baxpr commented May 24, 2018

Sure. Let me see if I can dig up some potential troublemakers. And I'll run a few things under the previous and new commits to make sure results are identical.

@baxpr
Copy link
Author

baxpr commented May 24, 2018

Compile-time warning:
nii_dicom_batch.cpp:2342:61: warning: data argument not used by format string

Looks like you want line 2342 to be
printMessage("Potential Alternative Intensity Scalings\n");

@drmclem
Copy link

drmclem commented May 24, 2018

Commenting on two comments back .... DV means that if you draw an ROI on the console and you use the DV values you will get the same answer - which of cousre is why the RS and Ri values are the same as the RWV scaling, We want people to measure the correct value on the scanner.

@neurolabusc
Copy link
Collaborator

@BaxterPRogers above you provided two PAR/REC copies of the same sequence. One is valid and one is missing slices. The valid solution saves 1648 slices (206 image positions, 2 cardiac triggers, 4 types [real, imaginary, magnitude, phase]) and describes a 864 mb REC file. The broken copy only saves 1544 slices (spatial positions 78..103 are stored only four times each rather than 8 times) and describes a 809 mb REC file. Obviously, no tool can recover the missing data from the broken file. Perhaps @drmclem can provide insight, but I urge you to understand how the corrupted file got created.

Correct header comments:

# Dataset name: Smith_20180405_06_01_10.40.12_(WIP_MP2RAGE_wip_TFEprepulsecardiac_Experiment1_SENSE) (convert_dicom_to_xmlrec.pl v0.4)
#
# CLINICAL TRYOUT             Research image export tool     V4.2

Broken header comments:

# Dataset name: E:\Export\\Smith_20180405_WIP_MP2RAGE_wip_TFEprepulsecardiac_Experiment1_SENSE_6_1
#
# CLINICAL TRYOUT             Research image export tool     V4.2

@baxpr
Copy link
Author

baxpr commented May 26, 2018

Thanks - I noticed that too. The corrupted file was a direct export from the scanner (made by a colleague) and I haven't yet tried to debug what happened. I'll try exporting it again when I sit down at the scanner next week.

@captainnova
Copy link
Collaborator

Hi, I ran my test suite with a freshly pulled dcm2niix today and found that this change produced some minor surprises in the file names. _real appeared in a Philips 2.6.3.6 DWI, some Siemens and Philips field maps, and a Philips 4.1.1 ASL. The field map file names are an improvement, but the _real appearing in the DWI and ASL was surprising. I believe they are both magnitude images (it is a little hard to tell with the ASL) and thus technically real, but usually in MRI magnitude is the default and "real" implies Gaussian instead of noncentral chi noise statistics. I could grumble at Philips since they set
(0008, 9208) Complex Image Component CS: 'REAL'
but this is old data.

Would it make sense to add a way to suppress adding _real, or _ph, etc.? I don't have a strong opinion, and only noticed it because my test script is very literal, and we may have to update some scripts to let dcm2niix take over the job of determining whether images are real, or phase, etc. Functionally, I wonder if the _ph, etc. are really needed if the -m y option is in use.

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

No branches or pull requests

4 participants