6

I'm trying to convert a slew of DTI siemens DICOMs to NifTi using freesurfer's dcm2nii utility but am failing on some files because they are missing the DiffusionGradientDirection tag (0x19,0x100E), which is necessary to generate the .bvec and .bval files. It's not that the tags don't have values, they don't appear to be there at all.

ds[0x19,0x100E] Traceback (most recent call last): File "", line 1, in File "/space/jazz/1/users/gwarner/anaconda/lib/python2.7/site-packages/pydicom-0.9.9-py2.7.egg/dicom/dataset.py", line 277, in getitem data_elem = dict.getitem(self, tag) KeyError: (0019, 100e)

I tried adding it but got the following error:

ds[0x19,0x100E].value = 'yes' Traceback (most recent call last): File "", line 1, in File "/space/jazz/1/users/gwarner/anaconda/lib/python2.7/site-packages/pydicom-0.9.9-py2.7.egg/dicom/dataset.py", line 277, in getitem data_elem = dict.getitem(self, tag) KeyError: (0019, 100e)

Is there a way I can manually insert this tag ?

Suever
  • 64,497
  • 14
  • 82
  • 101
G Warner
  • 1,309
  • 1
  • 15
  • 27
  • Are you sure the tag is right? Group 0x19 is odd, so it is a private tag, not part of the standard dictionary. And in the dicom standard, `Diffusion Gradient Direction` seems to be a Sequence, not a data element with a single value. – darcymason Jan 21 '17 at 04:01
  • @darcymason i bet op needs to add private owner – malat Jan 22 '17 at 08:39
  • @darcymason I am fairly certain that is the correct tag. I've tried this on a few thousand dti runs from various siemens machines and each one that has (0x19,0x100e) generates .bval/.bvec files successfully while every run that is missing that tag fails to generate the .bval/.bvec files. I believe all of the diffusion data is stored in private tags (http://mri-imaging.blogspot.com/2011/04/how-to-find-dti-information-from-dicom.html) – G Warner Jan 23 '17 at 16:40
  • Ah, I found it in pydicom's private tag info -- see full answer. – darcymason Jan 24 '17 at 02:18

2 Answers2

6

To add a new private data element in pydicom to a dataset ds, the add_new method can be used:

ds.add_new(tag, VR, value)

For this case, looking up the private tag in pydicom's _private_dict.py file (derived from gdcm's private tag info):

 'SIEMENS MR HEADER': {
    ...
    '0019xx0e': ('FD', '3', 'DiffusionGradientDirection', ''),

It is a repeating group kind of tag, where the xx can change to allow multiple data elements of this same type. Here FD is a double float, and 3 is the multiplicity (three values expected).

So in this case, to add the data element you need should look something like:

ds.add_new(0x19100e, 'FD', [0,1,0]) # I have no idea what this last vector should actually be

However, as malat pointed out, there needs to be a private creator tag as well to introduce the block, for the file to be valid DICOM. If it doesn't exist already you would likely have to add that also. Since you are converting the file to another format, perhaps you don't care if it works by adding only the single tag.

Once the data element has been added, you could change the value using ds[0x19100e].value = ... as in your original question.

As an aside, add_new is not needed for keywords that are in the standard dictionary; for those one can just directly set the item by name, e.g. ds.OtherPatientIDs='test', even if it does not yet exist in the dataset.

darcymason
  • 1,223
  • 6
  • 9
-1

I will need to use my crystal ball here to answer the question, since I do not have access to your DICOM DataSet, but I would bet this is failing for you because you do not have a good understanding on how Privates Tags works in DICOM.

I found a fairly well written page (only available in web.archive) that summarize the situation. In summary you need to double check the output of something like dcmdump and/or gdcmdump to guide you.

Let's synthetize what is going on here using one of the famous GDCMData sample file (but it should work the same for you).

$ gdcmdump SIEMENS_CSA2.dcm | grep 0019
(0019,0010) LO [SIEMENS MR HEADER ]                               # 18,1 Private Creator
(0019,1008) CS [IMAGE NUM 4 ]                                     # 12,1 CSA Image Header Type
(0019,1009) LO [1.0 ]                                             # 4,1 CSA Image Header Version ??
(0019,100b) DS [10632.5 ]                                         # 8,1 SliceMeasurementDuration
(0019,100f) SH [Fast]                                             # 4,1 GradientMode
(0019,1011) SH [No]                                               # 2,1 FlowCompensation
(0019,1012) SL 0\0\-2134                                          # 12,3 TablePositionOrigin
(0019,1013) SL 0\0\-2134                                          # 12,3 ImaAbsTablePosition
(0019,1014) IS [0\0\0 ]                                           # 6,3 ImaRelTablePosition
(0019,1015) FD -162.438\-61.4092\254.003                          # 24,3 SlicePosition_PCS
(0019,1017) DS [0.642857]                                         # 8,1 SliceResolution
(0019,1018) IS [7800]                                             # 4,1 RealDwellTime

As you can see above gdcmdump is able to tell that 0019,1018 is RealDwellTime

What happen now if we naively remove the private creator tag:

$ gdcmanon --dumb --remove 0019,0010 SIEMENS_CSA2.dcm /tmp/hack.dcm
$ gdcmdump /tmp/hack.dcm | grep 0019
(0019,1008) CS [IMAGE NUM 4 ]                                     # 12,? (1)  Private Element With Empty Private Creator
(0019,1009) LO [1.0 ]                                             # 4,? (1)  Private Element With Empty Private Creator
(0019,100b) DS [10632.5 ]                                         # 8,? (1)  Private Element With Empty Private Creator
(0019,100f) SH [Fast]                                             # 4,? (1)  Private Element With Empty Private Creator
(0019,1011) SH [No]                                               # 2,? (1)  Private Element With Empty Private Creator
(0019,1012) SL 0\0\-2134                                          # 12,? (3)  Private Element With Empty Private Creator
(0019,1013) SL 0\0\-2134                                          # 12,? (3)  Private Element With Empty Private Creator
(0019,1014) IS [0\0\0 ]                                           # 6,? (3)  Private Element With Empty Private Creator
(0019,1015) FD -162.438\-61.4092\254.003                          # 24,? (3)  Private Element With Empty Private Creator
(0019,1017) DS [0.642857]                                         # 8,? (1)  Private Element With Empty Private Creator
(0019,1018) IS [7800]                                             # 4,? (1)  Private Element With Empty Private Creator

Suddenly we have the weird situation where the DICOM attribute 0019,1018 is still present in the DataSet, but for 'some' reason gdcmdump is not able to tells us this is 'RealDwellTime'.

I am guessing this is the same issue for you, you are missing the key used for the private tags indirection (SIEMENS MR HEADER).


As a side note, did you check that the diffusion information is not stored directly in the CSA header, eg:

$ gdcmdump --csa my_input.dcm | grep -i diffusion

In that case I would report a bug to pydicom, so that they also parse this DICOM attribute to retrieve the diffusion information.

malat
  • 12,152
  • 13
  • 89
  • 158
  • Sorry, I'm a little confused. As I'm querying the data by tag number rather than tag name wouldn't `ds[0x19,0x100E]` still return a value? Also, I cannot see the tag when I just print out the entire header. Lastly, It wouldn't matter if the diffusion info is stored in the CSA header because the problem is that dcm2nii looks for it in the tags I mentioned and cannot be redirected to look elsewhere. The issue isn't so much my not being able to find the diffusion info but rather dcm2nii not being able to find it – G Warner Jan 23 '17 at 16:54