File: datadict.py

package info (click to toggle)
pydicom 1.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 15,164 kB
  • sloc: python: 31,583; sh: 218; makefile: 189
file content (281 lines) | stat: -rw-r--r-- 8,783 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# Copyright 2008-2018 pydicom authors. See LICENSE file for details.
# -*- coding: utf-8 -*-
"""Access dicom dictionary information"""

from pydicom.config import logger
from pydicom.tag import Tag, BaseTag

# the actual dict of {tag: (VR, VM, name, is_retired, keyword), ...}
from pydicom._dicom_dict import DicomDictionary

# those with tags like "(50xx, 0005)"
from pydicom._dicom_dict import RepeatersDictionary
from pydicom._private_dict import private_dictionaries
import warnings

# Generate mask dict for checking repeating groups etc.
# Map a true bitwise mask to the DICOM mask with "x"'s in it.
masks = {}
for mask_x in RepeatersDictionary:
    # mask1 is XOR'd to see that all non-"x" bits
    # are identical (XOR result = 0 if bits same)
    # then AND those out with 0 bits at the "x"
    # ("we don't care") location using mask2
    mask1 = int(mask_x.replace("x", "0"), 16)
    mask2 = int("".join(["F0" [c == "x"] for c in mask_x]), 16)
    masks[mask_x] = (mask1, mask2)


def mask_match(tag):
    for mask_x, (mask1, mask2) in masks.items():
        if (tag ^ mask1) & mask2 == 0:
            return mask_x
    return None


def add_dict_entry(tag, VR, keyword, description, VM='1', is_retired=''):
    """Update pydicom's DICOM dictionary with a new entry.

    Notes
    ----
    Dose not permanently update the dictionary,
    but only during run-time. Will replace an existing
    entry if the tag already exists in the dictionary.

    Parameters
    ----------
    tag : int
        The tag number for the new dictionary entry
    VR : str
        DICOM value representation
    description : str
        The descriptive name used in printing the entry.
        Often the same as the keyword, but with spaces between words.
    VM : str, optional
        DICOM value multiplicity. If not specified, then '1' is used.
    is_retired : str, optional
        Usually leave as blank string (default).
        Set to 'Retired' if is a retired data element.

    See Also
    --------
    pydicom.examples.add_dict_entry
        Example file which shows how to use this function
    add_dict_entries
        Update multiple values at once.

    Examples
    --------
    >>> from pydicom import Dataset
    >>> add_dict_entry(0x10011001, "UL", "TestOne", "Test One")
    >>> add_dict_entry(0x10011002, "DS", "TestTwo", "Test Two", VM='3')
    >>> ds = Dataset()
    >>> ds.TestOne = 'test'
    >>> ds.TestTwo = ['1', '2', '3']

    """
    new_dict_val = (VR, VM, description, is_retired, keyword)
    add_dict_entries({tag: new_dict_val})


def add_dict_entries(new_entries_dict):
    """Update pydicom's DICOM dictionary with new entries.

    Parameters
    ----------
    new_entries_dict : dict
        Dictionary of form:
        {tag: (VR, VM, description, is_retired, keyword),...}
        where parameters are as described in add_dict_entry

    See Also
    --------
    add_dict_entry
        Simpler function to add a single entry to the dictionary.

    Examples
    --------
    >>> from pydicom import Dataset
    >>> new_dict_items = {
    ...        0x10011001: ('UL', '1', "Test One", '', 'TestOne'),
    ...        0x10011002: ('DS', '3', "Test Two", '', 'TestTwo'),
    ... }
    >>> add_dict_entries(new_dict_items)
    >>> ds = Dataset()
    >>> ds.TestOne = 'test'
    >>> ds.TestTwo = ['1', '2', '3']

    >>> add_dict_entry(0x10011001, "UL", "TestOne", "Test One")
    >>> ds = Dataset()
    >>> ds.TestOne = 'test'
    """

    # Update the dictionary itself
    DicomDictionary.update(new_entries_dict)

    # Update the reverse mapping from name to tag
    new_names_dict = dict([(val[4], tag)
                           for tag, val in new_entries_dict.items()])
    keyword_dict.update(new_names_dict)


def get_entry(tag):
    """Return the tuple (VR, VM, name, is_retired, keyword)
    from the DICOM dictionary

    If the entry is not in the main dictionary,
    check the masked ones, e.g. repeating groups like 50xx, etc.
    """
    # Note: tried the lookup with 'if tag in DicomDictionary'
    # and with DicomDictionary.get, instead of try/except
    # Try/except was fastest using timeit if tag is valid (usual case)
    # My test had 5.2 usec vs 8.2 for 'contains' test, vs 5.32 for dict.get
    if not isinstance(tag, BaseTag):
        tag = Tag(tag)
    try:
        return DicomDictionary[tag]
    except KeyError:
        if not tag.is_private:
            mask_x = mask_match(tag)
            if mask_x:
                return RepeatersDictionary[mask_x]
        raise KeyError("Tag {0} not found in DICOM dictionary".format(tag))


def dictionary_is_retired(tag):
    """Return True if the dicom retired status
       is 'Retired' for the given tag"""
    if 'retired' in get_entry(tag)[3].lower():
        return True
    return False


def dictionary_VR(tag):
    """Return the dicom value representation
       for the given dicom tag."""
    return get_entry(tag)[0]


def dictionary_VM(tag):
    """Return the dicom value multiplicity
       for the given dicom tag."""
    return get_entry(tag)[1]


def dictionary_description(tag):
    """Return the descriptive text for the given dicom tag."""
    return get_entry(tag)[2]


def dictionary_keyword(tag):
    """Return the official DICOM standard
      (since 2011) keyword for the tag"""
    return get_entry(tag)[4]


def dictionary_has_tag(tag):
    """Return True if the dicom dictionary
       has an entry for the given tag."""
    return (tag in DicomDictionary)


def keyword_for_tag(tag):
    """Return the DICOM keyword for the given tag.

    Will return GroupLength for group length tags,
    and returns empty string ("") if the tag
    doesn't exist in the dictionary.
    """
    try:
        return dictionary_keyword(tag)
    except KeyError:
        return ""


# Provide for the 'reverse' lookup. Given the keyword, what is the tag?
logger.debug("Reversing DICOM dictionary so can look up tag from a keyword...")
keyword_dict = dict([(dictionary_keyword(tag), tag)
                     for tag in DicomDictionary])


def tag_for_keyword(keyword):
    """Return the dicom tag corresponding to keyword,
       or None if none exist."""
    return keyword_dict.get(keyword)


def tag_for_name(name):
    """Deprecated -- use tag_for_keyword"""
    msg = "tag_for_name is deprecated.  Use tag_for_keyword instead"
    warnings.warn(msg, DeprecationWarning)

    return tag_for_keyword(name)


def repeater_has_tag(tag):
    """Return True if the DICOM repeaters dictionary
       has an entry for `tag`."""
    return (mask_match(tag) in RepeatersDictionary)


REPEATER_KEYWORDS = [val[4] for val in RepeatersDictionary.values()]


def repeater_has_keyword(keyword):
    """Return True if the DICOM repeaters element
       exists with `keyword`."""
    return keyword in REPEATER_KEYWORDS


# PRIVATE DICTIONARY handling
# functions in analogy with those of main DICOM dict
def get_private_entry(tag, private_creator):
    """Return the tuple (VR, VM, name, is_retired)
       from a private dictionary"""
    if not isinstance(tag, BaseTag):
        tag = Tag(tag)
    try:
        private_dict = private_dictionaries[private_creator]
    except KeyError:
        msg = "Private creator {0} ".format(private_creator)
        msg += "not in private dictionary"
        raise KeyError(msg)

    # private elements are usually agnostic for
    # "block" (see PS3.5-2008 7.8.1 p44)
    # Some elements in _private_dict are explicit;
    # most have "xx" for high-byte of element
    # Try exact key first, but then try with "xx" in block position
    try:
        dict_entry = private_dict[tag]
    except KeyError:
        #  so here put in the "xx" in the block position for key to look up
        group_str = "%04x" % tag.group
        elem_str = "%04x" % tag.elem
        key = "%sxx%s" % (group_str, elem_str[-2:])
        if key not in private_dict:
            key = "%sxxxx%s" % (group_str[:2], elem_str[-2:])
            if key not in private_dict:
                msg = ("Tag {0} not in private dictionary "
                       "for private creator {1}".format(key, private_creator))
                raise KeyError(msg)
        dict_entry = private_dict[key]
    return dict_entry


def private_dictionary_VR(tag, private_creator):
    """Return the dicom value representation
       for the given dicom tag."""
    return get_private_entry(tag, private_creator)[0]


def private_dictionary_VM(tag, private_creator):
    """Return the dicom value multiplicity
       for the given dicom tag."""
    return get_private_entry(tag, private_creator)[1]


def private_dictionary_description(tag, private_creator):
    """Return the descriptive text
       for the given dicom tag."""
    return get_private_entry(tag, private_creator)[2]