5

When I read a dicom file all the private tags show up with VR of 'UN', instead of whatever they should be.

import pydicom
filename = 'H:\Fuji.dcm'
ds = pydicom.dcmread(filename)
print(ds)

Gives an output of:

...
(0009, 0010) Private Creator                     LO: 'FDMS 1.0'
(0009, 1005) [Image UID]                         UN: b'\x1d\xa6h\x12\x08\x07pZ\x0f9\x0e\xc2'
(0009, 1006) [Route Image UID]                   UN: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
(0009, 1008) [Image Display Information Version  UN: b'\x00\x00\x00\x00'
(0009, 1009) [Patient Information Version No.]   UN: b'\x00\x00\x00\x00'
(0009, 100c) [Film UID]                          UN: b'\x1d\xa6h\x12\x08\x07pZ\x0f9\x0e\xc2'
(0009, 1010) [Exposure Unit Type Code]           UN: b'F3'
(0009, 1080) [Kanji Hospital Name]               UN: None
(0009, 1090) [Distribution Code]                 UN: b'MAINPACS'
(0009, 10f0) [Blackening Process Flag]           UN: b'01'
(0009, 10f1) [Processing Information Flag]       UN: b'0001'
(0009, 10f2) Private tag data                    UN: b'01'
(0009, 10f3) Private tag data                    UN: b'01'
(0009, 10f4) Private tag data                    UN: b'01'
...

This doesn't change if I try to add them manually with:

pydicom.datadict.add_private_dict_entries('FDMS 1.0', 
                                          {0x00090005:('OW','1','Image UID'),
                                           0x00090006:('OW','1','Route Image UID'),
                                           0x00090008:('UL','1','Image Display Information Version No.'),
                                           0x00090009:('UL','1','Patient Information Version No.'),
                                           0x0009000c:('OW','1','Film UID'),
                                           0x00090010:('CS','1','Exposure Unit Type Code'),
                                           0x00090080:('LO','1','Kanji Hospital Name'),
                                           0x00090090:('ST','1','Distribution Code'),
                                           0x000900F0:('CS','1','Blackening Process Flag'),
                                           0x000900F1:('ST','1','Processing Information Flag'),
                                           0x000900F2:('CS','1','Normalization Flag'),
                                           0x000900F3:('CS','1','Tone characteristic'),
                                           0x000900F4:('CS','1','Window Value Fixed Flag'),
                                           })
print(ds)

It correctly picks up the descriptions of the missing tags, but still won't change their VR from UN:

...
(0009, 0010) Private Creator                     LO: 'FDMS 1.0'
(0009, 1005) [Image UID]                         UN: b'\x1d\xa6h\x12\x08\x07pZ\x0f9\x0e\xc2'
(0009, 1006) [Route Image UID]                   UN: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
(0009, 1008) [Image Display Information Version  UN: b'\x00\x00\x00\x00'
(0009, 1009) [Patient Information Version No.]   UN: b'\x00\x00\x00\x00'
(0009, 100c) [Film UID]                          UN: b'\x1d\xa6h\x12\x08\x07pZ\x0f9\x0e\xc2'
(0009, 1010) [Exposure Unit Type Code]           UN: b'F3'
(0009, 1080) [Kanji Hospital Name]               UN: None
(0009, 1090) [Distribution Code]                 UN: b'MAINPACS'
(0009, 10f0) [Blackening Process Flag]           UN: b'01'
(0009, 10f1) [Processing Information Flag]       UN: b'0001'
(0009, 10f2) [Normalization Flag]                UN: b'01'
(0009, 10f3) [Tone characteristic]               UN: b'01'
(0009, 10f4) [Window Value Fixed Flag]           UN: b'01'
...

This appears to be consistent behavior across all the clinical dicom files I've tried, and it doesn't make a difference whether I add the entries before or after I read the dicom file.

The file I used for this example is here:

https://drive.google.com/file/d/1SxDXG7cQQZQFluq88zPXUInMI_-6SsKK/view?usp=sharing

Thanks in advance for any help you can provide.

For anyone from the future:

I combined the code below from MrBean Bremen with some code from pydicom.values.convert_values to get the following function to use instead of dcmread:

def dcmread_convert_private(filename):
    ds = pydicom.dcmread(filename)
    for e in ds:
        if e.tag.is_private and e.VR == 'UN':
            try:
                index = (e.tag.element >> 12) - 1
                if index >= 0:
                    private_creators = ds.private_creators(e.tag.group)
                    if len(private_creators) > index:
                        private_creator = private_creators[index]
                        e.VR = pydicom.datadict.private_dictionary_VR(e.tag, private_creator)
                        # Following code taken from pydicom.values.convert_value
                        if e.value:
                            if isinstance(pydicom.values.converters[e.VR], tuple):
                                converter, num_format = pydicom.values.converters[e.VR]
                            else:
                                converter = pydicom.values.converters[e.VR]
                                num_format = None
                            byte_string = e.value
                            is_little_endian = ds.is_little_endian
                            is_implicit_VR = ds.is_implicit_VR
                            if e.VR in pydicom.charset.text_VRs or e.VR == 'PN':
                                e.value = converter(byte_string)
                            elif e.VR != "SQ":
                                e.value = converter(byte_string,
                                                  is_little_endian,
                                                  num_format)
                            else:
                                e.value = convert_SQ(byte_string,
                                                   is_implicit_VR,
                                                   is_little_endian,
                                                   e.value)
            except KeyError:
                pass
    return ds

I've only tested this on a few dicom files, so there are probably cases that aren't handled correctly. If I ever come across any, I'll try to remember to come back here and update it after I fix it.

2
  • This saved me a ton of time. Thanks for asking and for posting your solution. Commented Apr 23, 2021 at 14:11
  • No problem, glad I could help. Commented Apr 28, 2021 at 14:48

1 Answer 1

5

The problem is that the tags are already written as UN in the file, and that is saved as Explicit VR. I would guess that it has been written as Implicit VR originally and converted to Explicit VR later - during this conversion all unknown private tags get UN as VR.

The case that the private tags are registered, and the file re-read, is not handled in pydicom - the case of UN is only handled for non-private tags. So you are out of luck here.

I think that this is something that could be added to pydicom, though - I'm not sure if this could be a problem performance-wise, but it would make sense to file a respective issue (you could do it yourself, or I could do it if you want).

Here's a quick and dirty implementation to convert the tags in a dataset after reading it:

from pydicom import dcmread
from pydicom.datadict import private_dictionary_VR

ds = dcmread('xxx.dcm'))
for e in ds:
    if e.tag.is_private and e.VR == 'UN':
        try:
            index = (e.tag.element >> 12) - 1
            if index >= 0:
                private_creators = ds.private_creators(e.tag.group)
                if len(private_creators) > index:
                    private_creator = private_creators[index]
                    e.VR = private_dictionary_VR(e.tag, private_creator)
        except KeyError:
            pass
print(ds)

Update:
This is handled in a pydicom issue and is available since the pydicom 2.2 release.

6
  • Thanks for the reply. I submitted a feature request for some ability to force a conversion. Commented Feb 13, 2020 at 20:17
  • Is there any way to manually force a conversion for an individual tag? I could then just write a script to check them all and do the conversion. Commented Feb 13, 2020 at 20:29
  • Well, you could write a little conversion program, sure - basically iterate over the dataset, find all private tags with VR UN, lookup the private dictionary entry for them and replace them in the dataset if found, though you would need to get the private creator for each entry. I could put something together later if you want, don't have the time today. Commented Feb 13, 2020 at 20:39
  • That would be helpful, thanks. The only part I'm not sure about is how to re-encode the value from bytes to the various other types. I can reset the VR (eg. with ds[0x00091005].VR = 'OW'), but the value still shows as bytes (eg b'\x1d\xa6h\x12\x08\x07pZ\x0f9\x0e\xc2') Commented Feb 13, 2020 at 21:15
  • I added the code to the answer. Regarding VR OW: this a binary VR, and the value is supposed to show as bytes, so nothing unexpected here. Commented Feb 14, 2020 at 17:38

Not the answer you're looking for? Browse other questions tagged or ask your own question.