def correct_ambiguous_vr(ds, is_little_endian): """Iterate through `ds` correcting ambiguous VR elements (if possible). When it's not possible to correct the VR, the element will be returned unchanged. Currently the only ambiguous VR elements not corrected for are all retired or part of DICONDE, except for (60xx,3000) Overlay Data. If the VR is corrected and is 'US' or 'SS then the value will be updated using the pydicom.values.convert_numbers() method. Parameters ---------- ds : pydicom.dataset.Dataset The dataset containing ambiguous VR elements. is_little_endian : bool The byte ordering of the values in the dataset. Returns ------- ds : pydicom.dataset.Dataset The corrected dataset """ # Iterate through the elements for elem in ds: # Iterate the correction through any sequences if elem.VR == 'SQ': for item in elem: item = correct_ambiguous_vr(item, is_little_endian) if ' or ' in elem.VR: # OB or OW: 7fe0,0010 PixelData if elem.tag == 0x7fe00010: # If BitsAllocated is > 8 then OW, else may be OB or OW # As per PS3.5 Annex A.2. For BitsAllocated < 8 test the size # of each pixel to see if its written in OW or OB try: if ds.BitsAllocated > 8: elem.VR = 'OW' else: if len(ds.PixelData) / (ds.Rows * ds.Columns) == 2: elem.VR = 'OW' elif len(ds.PixelData) / (ds.Rows * ds.Columns) == 1: elem.VR = 'OB' except AttributeError: pass # These are all 'US or SS' # 0018,9810 ZeroVelocityPixelValue # 0022,1452 MappedPixelValue # 0028,0104 SmallestValidPixelValue (Retired) # 0028,0105 LargestValidPixelValue (Retired) # 0028,0106 SmallestImagePixelValue # 0028,0107 LargestImagePixelValue # 0028,0108 SmallestPixelValueInSeries # 0028,0109 LargestPixelValueInSeries # 0028,0110 SmallestImagePixelValueInPlane (Retired) # 0028,0111 LargestImagePixelValueInPlane (Retired) # 0028,0120 PixelPaddingValue # 0028,0121 PixelPaddingRangeLimit # 0028,1101 RedPaletteColorLookupTableDescriptor # 0028,1102 BluePaletteColorLookupTableDescriptor # 0028,1103 GreenPaletteColorLookupTableDescriptor # 0028,1111 LargeRedPaletteColorLookupTableDescriptor (Retired) # 0028,1112 LargeBluePaletteColorLookupTableDescriptor (Retired) # 0028,1113 LargeGreenPaletteColorLookupTableDescriptor (Retired) # 0028,3002 LUTDescriptor # 0040,9211 RealWorldValueLastValueMapped # 0040,9216 RealWorldValueFirstValueMapped # 0060,3004 HistogramFirstBinValue # 0060,3006 HistogramLastBinValue elif elem.tag in [ 0x00189810, 0x00221452, 0x00280104, 0x00280105, 0x00280106, 0x00280107, 0x00280108, 0x00280108, 0x00280110, 0x00280111, 0x00280120, 0x00280121, 0x00281101, 0x00281102, 0x00281103, 0x00283002, 0x00409211, 0x00409216, 0x00603004, 0x00603006 ]: # US if PixelRepresenation value is 0x0000, else SS # For references, see the list at # https://github.com/scaramallion/pynetdicom3/issues/3 if 'PixelRepresentation' in ds: if ds.PixelRepresentation == 0: elem.VR = 'US' byte_type = 'H' else: elem.VR = 'SS' byte_type = 'h' elem.value = convert_numbers(elem.value, is_little_endian, byte_type) # OB or OW: 5400,0110 ChannelMinimumValue # OB or OW: 5400,0112 ChannelMaximumValue # OB or OW: 5400,100A WaveformPaddingValue # OB or OW: 5400,1010 WaveformData elif elem.tag in [0x54000100, 0x54000112, 0x5400100A, 0x54001010]: # If WaveformBitsAllocated is > 8 then OW, otherwise may be # OB or OW, however not sure how to handle this. # See PS3.3 C.10.9.1. if 'WaveformBitsAllocated' in ds: if ds.WaveformBitsAllocated > 8: elem.VR = 'OW' # US or OW: 0028,3006 LUTData elif elem.tag in [0x00283006]: if 'LUTDescriptor' in ds: # First value in LUT Descriptor is how many values in # LUTData, if there's only one value then must be US # As per PS3.3 C.11.1.1.1 if ds.LUTDescriptor[0] == 1: elem.VR = 'US' elem.value = convert_numbers(elem.value, is_little_endian, 'H') else: elem.VR = 'OW' return ds
def correct_ambiguous_vr_element(elem, ds, is_little_endian): """Attempt to correct the ambiguous VR element `elem`. When it's not possible to correct the VR, the element will be returned unchanged. Currently the only ambiguous VR elements not corrected for are all retired or part of DICONDE. If the VR is corrected and is 'US' or 'SS' then the value will be updated using the pydicom.values.convert_numbers() method. Parameters ---------- elem : pydicom.dataelem.DataElement The element with an ambiguous VR. ds : pydicom.dataset.Dataset The dataset containing `elem`. is_little_endian : bool The byte ordering of the values in the dataset. Returns ------- elem : pydicom.dataelem.DataElement The corrected element """ if 'or' in elem.VR: # 'OB or OW': 7fe0,0010 PixelData if elem.tag == 0x7fe00010: try: # Compressed Pixel Data # PS3.5 Annex A.4 # If encapsulated, VR is OB and length is undefined if elem.is_undefined_length: elem.VR = 'OB' else: # Non-compressed Pixel Data # If BitsAllocated is > 8 then OW, else may be OB or OW # as per PS3.5 Annex A.2. For BitsAllocated < 8 test the # size of each pixel to see if its written in OW or OB if ds.BitsAllocated > 8: elem.VR = 'OW' else: if len(ds.PixelData) / (ds.Rows * ds.Columns) == 2: elem.VR = 'OW' elif len(ds.PixelData) / (ds.Rows * ds.Columns) == 1: elem.VR = 'OB' except AttributeError: pass # 'US or SS' and dependent on PixelRepresentation elif elem.tag in [ 0x00189810, 0x00221452, 0x00280104, 0x00280105, 0x00280106, 0x00280107, 0x00280108, 0x00280109, 0x00280110, 0x00280111, 0x00280120, 0x00280121, 0x00281101, 0x00281102, 0x00281103, 0x00283002, 0x00409211, 0x00409216, 0x00603004, 0x00603006 ]: # US if PixelRepresenation value is 0x0000, else SS # For references, see the list at # https://github.com/darcymason/pydicom/pull/298 if 'PixelRepresentation' in ds: if ds.PixelRepresentation == 0: elem.VR = 'US' byte_type = 'H' else: elem.VR = 'SS' byte_type = 'h' elem.value = convert_numbers(elem.value, is_little_endian, byte_type) # 'OB or OW' and dependent on WaveformBitsAllocated elif elem.tag in [0x54000100, 0x54000112, 0x5400100A, 0x54001010]: # If WaveformBitsAllocated is > 8 then OW, otherwise may be # OB or OW, however not sure how to handle this. # See PS3.3 C.10.9.1. if 'WaveformBitsAllocated' in ds: if ds.WaveformBitsAllocated > 8: elem.VR = 'OW' # 'US or OW': 0028,3006 LUTData elif elem.tag in [0x00283006]: if 'LUTDescriptor' in ds: # First value in LUT Descriptor is how many values in # LUTData, if there's only one value then must be US # As per PS3.3 C.11.1.1.1 if ds.LUTDescriptor[0] == 1: elem.VR = 'US' elem.value = convert_numbers(elem.value, is_little_endian, 'H') else: elem.VR = 'OW' # 'OB or OW': 60xx,3000 OverlayData and dependent on Transfer Syntax elif (elem.tag.group in range(0x6000, 0x601F, 2) and elem.tag.elem == 0x3000): # Implicit VR must be OW, explicit VR may be OB or OW # as per PS3.5 Section 8.1.2 and Annex A if hasattr(ds, 'is_implicit_VR') and ds.is_implicit_VR: elem.VR = 'OW' return elem
def _correct_ambiguous_vr_element(elem, ds, is_little_endian): """Implementation for `correct_ambiguous_vr_element`. See `correct_ambiguous_vr_element` for description. """ # 'OB or OW': 7fe0,0010 PixelData if elem.tag == 0x7fe00010: # Compressed Pixel Data # PS3.5 Annex A.4 # If encapsulated, VR is OB and length is undefined if elem.is_undefined_length: elem.VR = 'OB' # Non-compressed Pixel Data - Implicit Little Endian # PS3.5 Annex A1: VR is always OW elif ds.is_implicit_VR: elem.VR = 'OW' else: # Non-compressed Pixel Data - Explicit VR # PS3.5 Annex A.2: # If BitsAllocated is > 8 then VR shall be OW, # else may be OB or OW. # If we get here, the data has not been written before # or has been converted from Implicit Little Endian, # so we default to OB for BitsAllocated 1 or 8 elem.VR = 'OW' if ds.BitsAllocated > 8 else 'OB' # 'US or SS' and dependent on PixelRepresentation # (0018,9810) Zero Velocity Pixel Value # (0022,1452) Mapped Pixel Value # (0028,0104)/(0028,0105) Smallest/Largest Valid Pixel Value # (0028,0106)/(0028,0107) Smallest/Largest Image Pixel Value # (0028,0108)/(0028,0109) Smallest/Largest Pixel Value in Series # (0028,0110)/(0028,0111) Smallest/Largest Image Pixel Value in Plane # (0028,0120) Pixel Padding Value # (0028,0121) Pixel Padding Range Limit # (0028,1101-1103) Red/Green/Blue Palette Color Lookup Table Descriptor # (0028,3002) LUT Descriptor # (0040,9216)/(0040,9211) Real World Value First/Last Value Mapped # (0060,3004)/(0060,3006) Histogram First/Last Bin Value elif elem.tag in [ 0x00189810, 0x00221452, 0x00280104, 0x00280105, 0x00280106, 0x00280107, 0x00280108, 0x00280109, 0x00280110, 0x00280111, 0x00280120, 0x00280121, 0x00281101, 0x00281102, 0x00281103, 0x00283002, 0x00409211, 0x00409216, 0x00603004, 0x00603006 ]: # US if PixelRepresentation value is 0x0000, else SS # For references, see the list at # https://github.com/darcymason/pydicom/pull/298 # PixelRepresentation is usually set in the root dataset while 'PixelRepresentation' not in ds and ds.parent and ds.parent(): ds = ds.parent() # if no pixel data is present, none if these tags is used, # so we can just ignore a missing PixelRepresentation in this case if ('PixelRepresentation' not in ds and 'PixelData' not in ds or ds.PixelRepresentation == 0): elem.VR = 'US' byte_type = 'H' else: elem.VR = 'SS' byte_type = 'h' # Need to handle type check for elements with VM > 1 elem_value = elem.value if elem.VM == 1 else elem.value[0] if not isinstance(elem_value, int): elem.value = convert_numbers(elem.value, is_little_endian, byte_type) # 'OB or OW' and dependent on WaveformBitsAllocated # (5400, 0110) Channel Minimum Value # (5400, 0112) Channel Maximum Value # (5400, 100A) Waveform Padding Data # (5400, 1010) Waveform Data elif elem.tag in [0x54000110, 0x54000112, 0x5400100A, 0x54001010]: # If WaveformBitsAllocated is > 8 then OW, otherwise may be # OB or OW. # See PS3.3 C.10.9.1. if ds.is_implicit_VR: elem.VR = 'OW' else: elem.VR = 'OW' if ds.WaveformBitsAllocated > 8 else 'OB' # 'US or OW': 0028,3006 LUTData elif elem.tag == 0x00283006: # First value in LUT Descriptor is how many values in # LUTData, if there's only one value then must be US # As per PS3.3 C.11.1.1.1 if ds.LUTDescriptor[0] == 1: elem.VR = 'US' elem_value = elem.value if elem.VM == 1 else elem.value[0] if not isinstance(elem_value, int): elem.value = convert_numbers(elem.value, is_little_endian, 'H') else: elem.VR = 'OW' # 'OB or OW': 60xx,3000 OverlayData and dependent on Transfer Syntax elif (elem.tag.group in range(0x6000, 0x601F, 2) and elem.tag.elem == 0x3000): # Implicit VR must be OW, explicit VR may be OB or OW # as per PS3.5 Section 8.1.2 and Annex A elem.VR = 'OW' return elem
def correct_ambiguous_vr(ds, is_little_endian): """Iterate through `dataset` correct ambiguous VR elements. Also fixes the element.value as pydicom doesn't always handle decoding correctly. OB, string of bytes and insensitive to byte ordering OW, string of 16-bit words, sensitive to byte ordering SS, signed binary int, 16 bits in 2's complement. 2 byte fixed length. US, unsigned binary int. 2 byte fixed length. Elements with Unsolved Ambiguous VRs ------------------------------------ OB or OW 0014,3050 DarkCurrentCounts (DICONDE) OB or OW 0014,3070 AirCounts (DICONDE) US or SS 0028,0071 PerimeterValue (Retired) US or SS 0028,1100 GrayLookupTableDescriptor (Retired) US or SS or OW 0028,1200 GrayLookupTableData (Retired) OB or OW 50xx,200C AudioSampleData (Retired) OB or OW 50xx,3000 CurveData (Retired) OB or OW 60xx,3000 OverlayData OB or OW 7Fxx,0010 VariablePixelData (Retired) Parameters ---------- ds : pydicom.dataset.Dataset The dataset containing the elements with ambiguous VRs is_little_endian : bool Whether the dataset is encoded as little or big endian. Returns ------- ds : pydicom.dataset.Dataset A dataset with (hopefully) unambiguous VRs. Raises ------ ValueError If the ambiguous VR requires another element within the dataset to determine the VR to use, but this element is absent then ValueError will be raised. """ for elem in ds: # Iterate the correction through any sequences if elem.VR == 'SQ': for item in elem: item = correct_ambiguous_vr(item, is_little_endian) if ' or ' in elem.VR: # OB or OW: 7fe0,0010 PixelData if elem.tag == 0x7fe00010: # If BitsAllocated is > 8 then OW, else may be OB or OW # As per PS3.5 Annex A.2. For <= 8, test the size of each # pixel to see if its written in OW or OB try: if ds.BitsAllocated > 8: elem.VR = 'OW' else: if len(ds.PixelData) / (ds.Rows * ds.Columns) == 2: elem.VR = 'OW' elif len(ds.PixelData) / (ds.Rows * ds.Columns) == 1: elem.VR = 'OB' except AttributeError: raise ValueError("Cannot set VR for PixelData as a " "required element is missing. Consider " "using a implicit VR transfer syntax.") # US or SS: 0018,9810 ZeroVelocityPixelValue # US or SS: 0022,1452 MappedPixelValue # US or SS: 0028,0104 SmallestValidPixelValue (Retired) # US or SS: 0028,0105 LargestValidPixelValue (Retired) # US or SS: 0028,0106 SmallestImagePixelValue # US or SS: 0028,0107 LargestImagePixelValue # US or SS: 0028,0108 SmallestPixelValueInSeries # US or SS: 0028,0109 LargestPixelValueInSeries # US or SS: 0028,0110 SmallestImagePixelValueInPlane (Retired) # US or SS: 0028,0111 LargestImagePixelValueInPlane (Retired) # US or SS: 0028,0120 PixelPaddingValue # US or SS: 0028,0121 PixelPaddingRangeLimit # US or SS: 0028,1101 RedPaletteColorLookupTableDescriptor # US or SS: 0028,1102 BluePaletteColorLookupTableDescriptor # US or SS: 0028,1103 GreenPaletteColorLookupTableDescriptor # US or SS: 0028,3002 LUTDescriptor # US or SS: 0040,9211 RealWorldValueLastValueMapped # US or SS: 0040,9216 RealWorldValueFirstValueMapped # US or SS: 0060,3004 HistogramFirstBinValue # US or SS: 0060,3006 HistogramLastBinValue elif elem.tag in [ 0x00189810, 0x00221452, 0x00280104, 0x00280105, 0x00280106, 0x00280107, 0x00280108, 0x00280108, 0x00280110, 0x00280111, 0x00280120, 0x00280121, 0x00281101, 0x00281102, 0x00281103, 0x00283002, 0x00409211, 0x00409216, 0x00603004, 0x00603006 ]: # US if PixelRepresenation value is 0x0000, else SS # For references, see the list at # https://github.com/scaramallion/pynetdicom3/issues/3 if 'PixelRepresentation' in ds: if ds.PixelRepresentation == 0: elem.VR = 'US' byte_type = 'H' else: elem.VR = 'SS' byte_type = 'h' # Fix for pydicom not handling this correctly elem.value = convert_numbers(elem.value, is_little_endian, byte_type) else: raise ValueError("Cannot set VR of {} if " "PixelRepresentation is not in the" "dataset. Consider a transfer " "syntax with implicit VR.".format( elem.keyword)) # OB or OW: 5400,0110 ChannelMinimumValue # OB or OW: 5400,0112 ChannelMaximumValue # OB or OW: 5400,100A WaveformPaddingValue # OB or OW: 5400,1010 WaveformData elif elem.tag in [0x54000100, 0x54000112, 0x5400100A, 0x54001010]: # OB if WaveformSampleInterpretation value is # SB/UB/MB/AB, else OW. See the list at # https://github.com/scaramallion/pynetdicom3/issues/3 if 'WaveformBitsAllocated' in ds: if ds.WaveformBitsAllocated > 8: elem.VR = 'OW' else: raise ValueError("Cannot set VR of {} if " "WaveformBitsAllocated is <= 8. " "Consider using an implicit VR " "transfer syntax.".format( elem.keyword)) else: raise ValueError("Cannot set VR of {} if " "WaveformBitsAllocated is " "not in the dataset. Consider a transfer " "syntax with implicit VR.".format( elem.keyword)) # US or OW: 0028,3006 LUTData elif elem.tag in [0x00283006]: if 'LUTDescriptor' in ds: # First value in LUT Descriptor is how many values in # LUTData if ds.LUTDescriptor[0] == 1: elem.VR = 'US' elem.value = convert_numbers(elem.value, is_little_endian, 'H') else: elem.VR = 'OW' else: raise ValueError("Cannot set VR of LUTData if " "LUTDescriptor is not in the dataset. " "Consider using Implicit VR as the " "transfer syntax.") else: raise NotImplementedError("Cannot set VR of {} as the" " correct method for doing " "\n so is not known. Consider " "using a transfer syntax with " "implicit VR\n or " "setting the VR manually prior to " "sending.".format(elem.keyword)) LOGGER.debug("Setting VR of (%04x, %04x) %s to " "'%s'.", elem.tag.group, elem.tag.elem, elem.name, elem.VR) return ds
def _correct_ambiguous_vr_element(elem, ds, is_little_endian): """Implementation for `correct_ambiguous_vr_element`. See `correct_ambiguous_vr_element` for description. """ # 'OB or OW': 7fe0,0010 PixelData if elem.tag == 0x7fe00010: # Compressed Pixel Data # PS3.5 Annex A.4 # If encapsulated, VR is OB and length is undefined if elem.is_undefined_length: elem.VR = 'OB' # Non-compressed Pixel Data - Implicit Little Endian # PS3.5 Annex A1: VR is always OW elif ds.is_implicit_VR: elem.VR = 'OW' else: # Non-compressed Pixel Data - Explicit VR # PS3.5 Annex A.2: # If BitsAllocated is > 8 then VR shall be OW, # else may be OB or OW. # If we get here, the data has not been written before # or has been converted from Implicit Little Endian, # so we default to OB for BitsAllocated 1 or 8 elem.VR = 'OW' if ds.BitsAllocated > 8 else 'OB' # 'US or SS' and dependent on PixelRepresentation # (0018,9810) Zero Velocity Pixel Value # (0022,1452) Mapped Pixel Value # (0028,0104)/(0028,0105) Smallest/Largest Valid Pixel Value # (0028,0106)/(0028,0107) Smallest/Largest Image Pixel Value # (0028,0108)/(0028,0109) Smallest/Largest Pixel Value in Series # (0028,0110)/(0028,0111) Smallest/Largest Image Pixel Value in Plane # (0028,0120) Pixel Padding Value # (0028,0121) Pixel Padding Range Limit # (0028,1101-1103) Red/Green/Blue Palette Color Lookup Table Descriptor # (0028,3002) LUT Descriptor # (0040,9216)/(0040,9211) Real World Value First/Last Value Mapped # (0060,3004)/(0060,3006) Histogram First/Last Bin Value elif elem.tag in [ 0x00189810, 0x00221452, 0x00280104, 0x00280105, 0x00280106, 0x00280107, 0x00280108, 0x00280109, 0x00280110, 0x00280111, 0x00280120, 0x00280121, 0x00281101, 0x00281102, 0x00281103, 0x00283002, 0x00409211, 0x00409216, 0x00603004, 0x00603006 ]: # US if PixelRepresentation value is 0x0000, else SS # For references, see the list at # https://github.com/darcymason/pydicom/pull/298 if ds.PixelRepresentation == 0: elem.VR = 'US' byte_type = 'H' else: elem.VR = 'SS' byte_type = 'h' # Need to handle type check for elements with VM > 1 elem_value = elem.value if elem.VM == 1 else elem.value[0] if not isinstance(elem_value, int): elem.value = convert_numbers(elem.value, is_little_endian, byte_type) # 'OB or OW' and dependent on WaveformBitsAllocated # (5400, 0110) Channel Minimum Value # (5400, 0112) Channel Maximum Value # (5400, 100A) Waveform Padding Data # (5400, 1010) Waveform Data elif elem.tag in [0x54000110, 0x54000112, 0x5400100A, 0x54001010]: # If WaveformBitsAllocated is > 8 then OW, otherwise may be # OB or OW. # See PS3.3 C.10.9.1. if ds.is_implicit_VR: elem.VR = 'OW' else: elem.VR = 'OW' if ds.WaveformBitsAllocated > 8 else 'OB' # 'US or OW': 0028,3006 LUTData elif elem.tag == 0x00283006: # First value in LUT Descriptor is how many values in # LUTData, if there's only one value then must be US # As per PS3.3 C.11.1.1.1 if ds.LUTDescriptor[0] == 1: elem.VR = 'US' elem_value = elem.value if elem.VM == 1 else elem.value[0] if not isinstance(elem_value, int): elem.value = convert_numbers(elem.value, is_little_endian, 'H') else: elem.VR = 'OW' # 'OB or OW': 60xx,3000 OverlayData and dependent on Transfer Syntax elif (elem.tag.group in range(0x6000, 0x601F, 2) and elem.tag.elem == 0x3000): # Implicit VR must be OW, explicit VR may be OB or OW # as per PS3.5 Section 8.1.2 and Annex A elem.VR = 'OW' return elem
def correct_ambiguous_vr_element(elem, ds, is_little_endian): """Attempt to correct the ambiguous VR element `elem`. When it's not possible to correct the VR, the element will be returned unchanged. Currently the only ambiguous VR elements not corrected for are all retired or part of DICONDE. If the VR is corrected and is 'US' or 'SS' then the value will be updated using the pydicom.values.convert_numbers() method. Parameters ---------- elem : pydicom.dataelem.DataElement The element with an ambiguous VR. ds : pydicom.dataset.Dataset The dataset containing `elem`. is_little_endian : bool The byte ordering of the values in the dataset. Returns ------- elem : pydicom.dataelem.DataElement The corrected element """ if 'or' in elem.VR: # convert raw data elements before handling them if elem.is_raw: elem = DataElement_from_raw(elem) ds.__setitem__(elem.tag, elem) # 'OB or OW': 7fe0,0010 PixelData if elem.tag == 0x7fe00010: # Compressed Pixel Data # PS3.5 Annex A.4 # If encapsulated, VR is OB and length is undefined if elem.is_undefined_length: elem.VR = 'OB' # Non-compressed Pixel Data - Implicit Little Endian # PS3.5 Annex A1: VR is always OW elif ds.is_implicit_VR: elem.VR = 'OW' else: # Non-compressed Pixel Data - Explicit VR # PS3.5 Annex A.2: # If BitsAllocated is > 8 then VR shall be OW, # else may be OB or OW. # If we get here, the data has not been written before # or has been converted from Implicit Little Endian, # so we default to OB for BitsAllocated 1 or 8 elem.VR = 'OW' if ds.BitsAllocated > 8 else 'OB' # 'US or SS' and dependent on PixelRepresentation # (0018,9810) Zero Velocity Pixel Value # (0022,1452) Mapped Pixel Value # (0028,0104)/(0028,0105) Smallest/Largest Valid Pixel Value # (0028,0106)/(0028,0107) Smallest/Largest Image Pixel Value # (0028,0108)/(0028,0109) Smallest/Largest Pixel Value in Series # (0028,0110)/(0028,0111) Smallest/Largest Image Pixel Value in Plane # (0028,0120) Pixel Padding Value # (0028,0121) Pixel Padding Range Limit # (0028,1101-1103) Red/Green/Blue Palette Color Lookup Table Descriptor # (0028,3002) LUT Descriptor # (0040,9216)/(0040,9211) Real World Value First/Last Value Mapped # (0060,3004)/(0060,3006) Histogram First/Last Bin Value elif elem.tag in [ 0x00189810, 0x00221452, 0x00280104, 0x00280105, 0x00280106, 0x00280107, 0x00280108, 0x00280109, 0x00280110, 0x00280111, 0x00280120, 0x00280121, 0x00281101, 0x00281102, 0x00281103, 0x00283002, 0x00409211, 0x00409216, 0x00603004, 0x00603006 ]: # US if PixelRepresentation value is 0x0000, else SS # For references, see the list at # https://github.com/darcymason/pydicom/pull/298 if ds.PixelRepresentation == 0: elem.VR = 'US' byte_type = 'H' else: elem.VR = 'SS' byte_type = 'h' elem.value = convert_numbers(elem.value, is_little_endian, byte_type) # 'OB or OW' and dependent on WaveformBitsAllocated # (5400, 0110) Channel Minimum Value # (5400, 0112) Channel Maximum Value # (5400, 100A) Waveform Padding Data # (5400, 1010) Waveform Data elif elem.tag in [0x54000110, 0x54000112, 0x5400100A, 0x54001010]: # If WaveformBitsAllocated is > 8 then OW, otherwise may be # OB or OW. # See PS3.3 C.10.9.1. if ds.is_implicit_VR: elem.VR = 'OW' else: elem.VR = 'OW' if ds.WaveformBitsAllocated > 8 else 'OB' # 'US or OW': 0028,3006 LUTData elif elem.tag == 0x00283006: # First value in LUT Descriptor is how many values in # LUTData, if there's only one value then must be US # As per PS3.3 C.11.1.1.1 if ds.LUTDescriptor[0] == 1: elem.VR = 'US' elem.value = convert_numbers(elem.value, is_little_endian, 'H') else: elem.VR = 'OW' # 'OB or OW': 60xx,3000 OverlayData and dependent on Transfer Syntax elif (elem.tag.group in range(0x6000, 0x601F, 2) and elem.tag.elem == 0x3000): # Implicit VR must be OW, explicit VR may be OB or OW # as per PS3.5 Section 8.1.2 and Annex A elem.VR = 'OW' return elem
def _correct_ambiguous_vr_element(elem: DataElement, ds: Dataset, is_little_endian: bool) -> DataElement: """Implementation for `correct_ambiguous_vr_element`. See `correct_ambiguous_vr_element` for description. """ # 'OB or OW': 7fe0,0010 PixelData if elem.tag == 0x7fe00010: # Compressed Pixel Data # PS3.5 Annex A.4 # If encapsulated, VR is OB and length is undefined if elem.is_undefined_length: elem.VR = VR.OB elif ds.is_implicit_VR: # Non-compressed Pixel Data - Implicit Little Endian # PS3.5 Annex A1: VR is always OW elem.VR = VR.OW else: # Non-compressed Pixel Data - Explicit VR # PS3.5 Annex A.2: # If BitsAllocated is > 8 then VR shall be OW, # else may be OB or OW. # If we get here, the data has not been written before # or has been converted from Implicit Little Endian, # so we default to OB for BitsAllocated 1 or 8 elem.VR = VR.OW if cast(int, ds.BitsAllocated) > 8 else VR.OB # 'US or SS' and dependent on PixelRepresentation elif elem.tag in _us_ss_tags: # US if PixelRepresentation value is 0x0000, else SS # For references, see the list at # https://github.com/darcymason/pydicom/pull/298 # PixelRepresentation is usually set in the root dataset while 'PixelRepresentation' not in ds and ds.parent and ds.parent(): ds = cast(Dataset, ds.parent()) # if no pixel data is present, none if these tags is used, # so we can just ignore a missing PixelRepresentation in this case if ('PixelRepresentation' not in ds and 'PixelData' not in ds or ds.PixelRepresentation == 0): elem.VR = VR.US byte_type = 'H' else: elem.VR = VR.SS byte_type = 'h' if elem.VM == 0: return elem # Need to handle type check for elements with VM > 1 elem_value = (elem.value if elem.VM == 1 else cast( Sequence[Any], elem.value)[0]) if not isinstance(elem_value, int): elem.value = convert_numbers(cast(bytes, elem.value), is_little_endian, byte_type) # 'OB or OW' and dependent on WaveformBitsAllocated elif elem.tag in _ob_ow_tags: # If WaveformBitsAllocated is > 8 then OW, otherwise may be # OB or OW. # See PS3.3 C.10.9.1. if ds.is_implicit_VR: elem.VR = VR.OW else: elem.VR = (VR.OW if cast(int, ds.WaveformBitsAllocated) > 8 else VR.OB) # 'US or OW': 0028,3006 LUTData elif elem.tag == 0x00283006: # First value in LUT Descriptor is how many values in # LUTData, if there's only one value then must be US # As per PS3.3 C.11.1.1.1 if cast(Sequence[int], ds.LUTDescriptor)[0] == 1: elem.VR = VR.US if elem.VM == 0: return elem elem_value = (elem.value if elem.VM == 1 else cast( Sequence[Any], elem.value)[0]) if not isinstance(elem_value, int): elem.value = convert_numbers(cast(bytes, elem.value), is_little_endian, 'H') else: elem.VR = VR.OW # 'OB or OW': 60xx,3000 OverlayData and dependent on Transfer Syntax elif elem.tag in _overlay_data_tags: # Implicit VR must be OW, explicit VR may be OB or OW # as per PS3.5 Section 8.1.2 and Annex A elem.VR = VR.OW return elem