/usr/share/pyshared/dicom/dataelem.py is in python-dicom 0.9.6-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | # dataelem.py
"""Define the DataElement class - elements within a dataset.
DataElements have a DICOM value representation VR, a value multiplicity VM,
and a value.
"""
#
# Copyright (c) 2008 Darcy Mason
# This file is part of pydicom, released under a modified MIT license.
# See the file license.txt included with this distribution, also
# available at http://pydicom.googlecode.com
#
import logging
logger = logging.getLogger('pydicom')
from dicom.datadict import dictionaryHasTag, dictionaryDescription
from dicom.datadict import private_dictionaryDescription, dictionaryVR
from dicom.tag import Tag
from dicom.UID import UID
from dicom.valuerep import PersonName
try:
from collections import namedtuple
except ImportError: # for python <2.6
from dicom.util.namedtup import namedtuple
# os.stat is only available on Unix and Windows
# Not sure if on other platforms the import fails, or the call to it??
stat_available = True
try:
from os import stat
except:
stat_available = False
import os.path
from dicom.filebase import DicomFile
import warnings
# Helper functions:
def isMultiValue(value):
"""Helper function: return True if 'value' is 'list-like'."""
if isString(value):
return False
try:
iter(value)
except TypeError:
return False
return True
def isString(val):
"""Helper function: return True if val is a string."""
try:
val + ""
except:
return False
return True
def isStringOrStringList(val):
"""Return true if val consists only of strings. val may be a list/tuple."""
if isMultiValue(val):
for item in val:
if not isString(item):
return False
return True
else: # single value - test for a string
return isString(val)
_backslash = "\\" # double '\' because it is used as escape chr in Python
class DataElement(object):
"""Contain and manipulate a Dicom data element, having a tag, VR, VM and value.
Most user code will not create data elements using this class directly,
but rather through 'named tags' in Dataset objects.
See the Dataset class for a description of how Datasets, Sequences,
and DataElements work.
Class Data
----------
For string display (via __str__), the following are used:
descripWidth -- maximum width of description field (default 35).
maxBytesToDisplay -- longer data will display "array of # bytes" (default 16).
showVR -- True (default) to include the dicom VR just before the value.
"""
descripWidth = 35
maxBytesToDisplay = 16
showVR = 1
def __init__(self, tag, VR, value, file_value_tell=None,
is_undefined_length=False):
"""Create a data element instance.
Most user code should instead use 'Named tags' (see Dataset class)
to create data_elements, for which only the value is supplied,
and the VR and tag are determined from the dicom dictionary.
tag -- dicom (group, element) tag in any form accepted by Tag().
VR -- dicom value representation (see DICOM standard part 6)
value -- the value of the data element. One of the following:
- a single string value
- a number
- a list or tuple with all strings or all numbers
- a multi-value string with backslash separator
file_value_tell -- used internally by Dataset, to store the write
position for ReplaceDataElementValue method
is_undefined_length -- used internally to store whether the length
field in this data element was 0xFFFFFFFFL, i.e. "undefined length"
"""
self.tag = Tag(tag)
self.VR = VR # Note!: you must set VR before setting value
self.value = value
self.file_tell = file_value_tell
self.is_undefined_length = is_undefined_length
def _getvalue(self):
"""Get method for 'value' property"""
return self._value
def _setvalue(self, val):
"""Set method for 'value' property"""
# Check if is a string with multiple values separated by '\'
# If so, turn them into a list of separate strings
if isString(val) and self.VR not in \
['UT','ST','LT', 'FL','FD','AT','OB','OW','OF','SL','SQ','SS',
'UL', 'OB/OW', 'OW/OB', 'OB or OW', 'OW or OB', 'UN'] and 'US' not in self.VR: # latter covers 'US or SS' etc
if _backslash in val:
val = val.split(_backslash)
self._value = self._convert_value(val)
if self.VR in ['IS', 'DS']: # a number as a text string
# If IS/DS need to store number but keep string also
# If already a string, str(..) will have not change it
if self.VM > 1:
self.string_value = [str(x) for x in val]
else:
self.string_value = str(val)
value = property(_getvalue, _setvalue, doc=
"""The value (possibly multiple values) of this data_element.""")
def _getVM(self):
"""Get method for VM property"""
if isMultiValue(self.value):
return len(self.value)
else:
return 1
VM = property(_getVM, doc =
"""The number of values in the data_element's 'value'""")
def _convert_value(self, val):
"""Convert Dicom string values if possible to e.g. numbers. Handle the case
of multiple value data_elements"""
if self.VR=='SQ': # a sequence - leave it alone
return val
# if the value is a list, convert each element
try:
val.append
except AttributeError: # not a list
return self._convert(val)
else:
returnvalue = []
for subval in val:
returnvalue.append(self._convert(subval))
return returnvalue
def _convert(self, val):
"""Take the value and convert to number, etc if possible"""
try:
if self.VR in ['IS'] and val:
return int(str(val)) # str(val) so does not truncate a float without error
elif self.VR in ['DS'] and val:
return float(val)
elif self.VR == "UI":
return UID(val)
# Later may need this for PersonName as for UI,
# but needs more thought
# elif self.VR == "PN":
# return PersonName(val)
else: # is either a string or a type 2 optionally blank string
return val # this means a "numeric" value could be empty string ""
except TypeError:
print "Could not convert value '%s' to VR '%s' in tag %s" \
% (repr(val), self.VR, self.tag)
except ValueError:
print "Could not convert value '%s' to VR '%s' in tag %s" \
% (repr(val), self.VR, self.tag)
def __str__(self):
"""Return str representation of this data_element"""
repVal = self.repval
if self.showVR:
s = "%s %-*s %s: %s" % (str(self.tag), self.descripWidth,
self.description()[:self.descripWidth], self.VR, repVal)
else:
s = "%s %-*s %s" % (str(self.tag), self.descripWidth,
self.description()[:self.descripWidth], repVal)
return s
def _get_repval(self):
"""Return a str representation of the current value for use in __str__"""
if (self.VR in ['OB', 'OW', 'OW/OB', 'OW or OB', 'OB or OW', 'US or SS or OW', 'US or SS']
and len(self.value) > self.maxBytesToDisplay):
repVal = "Array of %d bytes" % len(self.value)
elif hasattr(self, 'string_value'): # for VR of IS or DS
repVal = repr(self.string_value)
elif isinstance(self.value, UID):
repVal = self.value.name
else:
repVal = repr(self.value) # will tolerate unicode too
return repVal
repval = property(_get_repval)
def __unicode__(self):
"""Return unicode representation of this data_element"""
if isinstance(self.value, unicode):
# start with the string rep then replace the value part with the unicode
strVal = str(self)
uniVal = unicode(strVal.replace(self.repval, "")) + self.value
return uniVal
else:
return unicode(str(self))
def __getitem__(self, key):
"""Returns the item from my value's Sequence, if it is one."""
try:
return self.value[key]
except TypeError:
raise TypeError, "DataElement value is unscriptable (not a Sequence)"
def _get_name(self):
return self.description()
name = property(_get_name)
def description(self):
"""Return the DICOM dictionary description for this dicom tag."""
if dictionaryHasTag(self.tag):
name = dictionaryDescription(self.tag)
elif self.tag.is_private:
name = "Private tag data" # default
if hasattr(self, 'private_creator'):
try:
# If have name from private dictionary, use it, but
# but put in square brackets so is differentiated,
# and clear that cannot access it by name
name = "[" + private_dictionaryDescription(self.tag, self.private_creator) + "]"
except KeyError:
pass
elif self.tag.elem >> 8 == 0:
name = "Private Creator"
elif self.tag.element == 0: # implied Group Length dicom versions < 3
name = "Group Length"
else:
name = ""
return name
def __repr__(self):
"""Handle repr(data_element)"""
if self.VR == "SQ":
return repr(self.value)
else:
return str(self)
class DeferredDataElement(DataElement):
"""Subclass of DataElement where value is not read into memory until needed"""
def __init__(self, tag, VR, fp, file_mtime, data_element_tell, length):
"""Store basic info for the data element but value will be read later
fp -- DicomFile object representing the dicom file being read
file_mtime -- last modification time on file, used to make sure
it has not changed since original read
data_element_tell -- file position at start of data element,
(not the start of the value part, but start of whole element)
"""
self.tag = Tag(tag)
self.VR = VR
self._value = None # flag as unread
# Check current file object and save info needed for read later
# if not isinstance(fp, DicomFile):
# raise NotImplementedError, "Deferred read is only available for DicomFile objects"
self.fp_is_implicit_VR = fp.is_implicit_VR
self.fp_is_little_endian = fp.is_little_endian
self.filepath = fp.name
self.file_mtime = file_mtime
self.data_element_tell = data_element_tell
self.length = length
def _get_repval(self):
if self._value is None:
return "Deferred read: length %d" % self.length
else:
return DataElement._get_repval(self)
repval = property(_get_repval)
def _getvalue(self):
"""Get method for 'value' property"""
# Must now read the value if haven't already
if self._value is None:
self.read_value()
return DataElement._getvalue(self)
def _setvalue(self, val):
DataElement._setvalue(self, val)
value = property(_getvalue, _setvalue)
RawDataElement = namedtuple('RawDataElement',
'tag VR length value value_tell is_implicit_VR is_little_endian')
def DataElement_from_raw(raw_data_element):
"""Return a DataElement from a RawDataElement"""
from dicom.values import convert_value # XXX buried here to avoid circular import filereader->Dataset->convert_value->filereader (for SQ parsing)
raw = raw_data_element
VR = raw.VR
if VR is None: # Can be if was implicit VR
try:
VR = dictionaryVR(raw.tag)
except KeyError:
if raw.tag.is_private:
VR = 'OB' # just read the bytes, no way to know what they mean
elif raw.tag.element == 0: # group length tag implied in versions < 3.0
VR = 'UL'
else:
raise KeyError, "Unknown DICOM tag %s - can't look up VR" % str(raw.tag)
try:
value = convert_value(VR, raw)
except NotImplementedError, e:
raise NotImplementedError, "%s in tag %r" % (str(e), raw.tag)
return DataElement(raw.tag, VR, value, raw.value_tell, raw.length==0xFFFFFFFFL)
class Attribute(DataElement):
"""Deprecated -- use DataElement instead"""
def __init__(self, tag, VR, value, file_value_tell=None):
warnings.warn("The Attribute class is deprecated and will be removed in pydicom 1.0. Use DataElement", DeprecationWarning)
DataElement.__init__(self, tag, VR, value, file_value_tell)
|