Ejemplo n.º 1
0
def convert_numbers(
    byte_string: bytes,
    is_little_endian: bool,
    struct_format: str
) -> Union[str, int, float, MutableSequence[int], MutableSequence[float]]:
    """Return a decoded numerical VR value.

    Given an encoded DICOM Element value, use `struct_format` and the
    endianness of the data to decode it.

    Parameters
    ----------
    byte_string : bytes
        The encoded numerical VR element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str
        The format of the numerical data encoded in `byte_string`. Should be a
        valid format for :func:`struct.unpack()` without the endianness.

    Returns
    -------
    str
        If there is no encoded data in `byte_string` then an empty string will
        be returned.
    value
        If `byte_string` encodes a single value then it will be returned.
    list
        If `byte_string` encodes multiple values then a list of the decoded
        values will be returned.
    """
    endianChar = '><'[is_little_endian]

    # "=" means use 'standard' size, needed on 64-bit systems.
    bytes_per_value = calcsize("=" + struct_format)
    length = len(byte_string)

    if length % bytes_per_value != 0:
        raise BytesLengthException(
            "Expected total bytes to be an even multiple of bytes per value. "
            f"Instead received {byte_string!r} with length {length} and "
            f"struct format '{struct_format}' which corresponds to bytes per "
            f"value of {bytes_per_value}."
        )

    format_string = f"{endianChar}{length // bytes_per_value}{struct_format}"
    value: Union[Tuple[int, ...], Tuple[float, ...]] = (
        unpack(format_string, byte_string)
    )

    # if the number is empty, then return the empty
    # string rather than empty list
    if len(value) == 0:
        return ''

    if len(value) == 1:
        return value[0]

    # convert from tuple to a list so can modify if need to
    return list(value)
Ejemplo n.º 2
0
def DataElement_from_raw(raw_data_element: RawDataElement,
                         encoding: Optional[Union[
                             str, MutableSequence[str]]] = None,
                         dataset: Optional["Dataset"] = None) -> DataElement:
    """Return a :class:`DataElement` created from `raw_data_element`.

    Parameters
    ----------
    raw_data_element : RawDataElement
        The raw data to convert to a :class:`DataElement`.
    encoding : str or list of str, optional
        The character encoding of the raw data.
    dataset : Dataset, optional
        If given, used to resolve the VR for known private tags.

    Returns
    -------
    DataElement

    Raises
    ------
    KeyError
        If `raw_data_element` belongs to an unknown non-private tag and
        :attr:`~pydicom.config.settings.reading_validation_mode` is set
        to ``RAISE``.
    """
    # XXX buried here to avoid circular import
    # filereader->Dataset->convert_value->filereader
    # (for SQ parsing)

    from pydicom.values import convert_value
    raw = raw_data_element

    # If user has hooked into conversion of raw values, call his/her routine
    if config.data_element_callback:
        raw = config.data_element_callback(
            raw_data_element,
            encoding=encoding,
            **config.data_element_callback_kwargs)

    vr = raw.VR
    if vr is None:  # Can be if was implicit VR
        try:
            vr = dictionary_VR(raw.tag)
        except KeyError:
            # just read the bytes, no way to know what they mean
            if raw.tag.is_private:
                # for VR for private tags see PS3.5, 6.2.2
                vr = _private_vr_for_tag(dataset, raw.tag)

            # group length tag implied in versions < 3.0
            elif raw.tag.element == 0:
                vr = VR_.UL
            else:
                msg = f"Unknown DICOM tag {str(raw.tag)}"
                if config.settings.reading_validation_mode == config.RAISE:
                    raise KeyError(msg + " can't look up VR")

                vr = VR_.UN
                warnings.warn(msg + " - setting VR to 'UN'")
    elif vr == VR_.UN and config.replace_un_with_known_vr:
        # handle rare case of incorrectly set 'UN' in explicit encoding
        # see also DataElement.__init__()
        if raw.tag.is_private:
            vr = _private_vr_for_tag(dataset, raw.tag)
        elif raw.value is None or len(raw.value) < 0xffff:
            try:
                vr = dictionary_VR(raw.tag)
            except KeyError:
                pass
    try:
        value = convert_value(vr, raw, encoding)
    except NotImplementedError as e:
        raise NotImplementedError(f"{str(e)} in tag {raw.tag!r}")
    except BytesLengthException as e:
        message = (
            f"{e} This occurred while trying to parse {raw.tag} according "
            f"to VR '{vr}'.")
        if config.convert_wrong_length_to_UN:
            warnings.warn(f"{message} Setting VR to 'UN'.")
            vr = VR_.UN
            value = raw.value
        else:
            raise BytesLengthException(
                f"{message} To replace this error with a warning set "
                "pydicom.config.convert_wrong_length_to_UN = True.")

    if raw.tag in _LUT_DESCRIPTOR_TAGS and value:
        # We only fix the first value as the third value is 8 or 16
        try:
            if value[0] < 0:
                value[0] += 65536
        except TypeError:
            pass

    return DataElement(
        raw.tag,
        vr,
        value,
        raw.value_tell,
        raw.length == 0xFFFFFFFF,
        already_converted=True,
    )
Ejemplo n.º 3
0
def DataElement_from_raw(
    raw_data_element: RawDataElement, encoding: Optional[List[str]] = None
) -> DataElement:
    """Return a :class:`DataElement` created from `raw_data_element`.

    Parameters
    ----------
    raw_data_element : RawDataElement
        The raw data to convert to a :class:`DataElement`.
    encoding : list of str, optional
        The character encoding of the raw data.

    Returns
    -------
    DataElement

    Raises
    ------
    KeyError
        If `raw_data_element` belongs to an unknown non-private tag and
        `config.enforce_valid_values` is set.
    """
    # XXX buried here to avoid circular import
    # filereader->Dataset->convert_value->filereader
    # (for SQ parsing)

    from pydicom.values import convert_value
    raw = raw_data_element

    # If user has hooked into conversion of raw values, call his/her routine
    if config.data_element_callback:
        raw = config.data_element_callback(
            raw_data_element,
            encoding=encoding,
            **config.data_element_callback_kwargs
        )

    VR = raw.VR
    if VR is None:  # Can be if was implicit VR
        try:
            VR = dictionary_VR(raw.tag)
        except KeyError:
            # just read the bytes, no way to know what they mean
            if raw.tag.is_private:
                # for VR for private tags see PS3.5, 6.2.2
                if raw.tag.is_private_creator:
                    VR = 'LO'
                else:
                    VR = 'UN'

            # group length tag implied in versions < 3.0
            elif raw.tag.element == 0:
                VR = 'UL'
            else:
                msg = "Unknown DICOM tag {0:s}".format(str(raw.tag))
                if config.enforce_valid_values:
                    msg += " can't look up VR"
                    raise KeyError(msg)
                else:
                    VR = 'UN'
                    msg += " - setting VR to 'UN'"
                    warnings.warn(msg)
    elif (VR == 'UN' and not raw.tag.is_private and
          config.replace_un_with_known_vr):
        # handle rare case of incorrectly set 'UN' in explicit encoding
        # see also DataElement.__init__()
        if (
            raw.length == 0xffffffff
            or raw.value is None
            or len(raw.value) < 0xffff
        ):
            try:
                VR = dictionary_VR(raw.tag)
            except KeyError:
                pass
    try:
        value = convert_value(VR, raw, encoding)
    except NotImplementedError as e:
        raise NotImplementedError("{0:s} in tag {1!r}".format(str(e), raw.tag))
    except BytesLengthException as e:
        message = (f"{e} This occurred while trying to parse "
                   f"{raw.tag} according to VR '{VR}'.")
        if config.convert_wrong_length_to_UN:
            warnings.warn(f"{message} Setting VR to 'UN'.")
            VR = "UN"
            value = raw.value
        else:
            raise BytesLengthException(
                f"{message} To replace this error with a warning set "
                "pydicom.config.convert_wrong_length_to_UN = True."
            )

    if raw.tag in _LUT_DESCRIPTOR_TAGS and value:
        # We only fix the first value as the third value is 8 or 16
        try:
            if value[0] < 0:
                value[0] += 65536
        except TypeError:
            pass

    return DataElement(raw.tag, VR, value, raw.value_tell,
                       raw.length == 0xFFFFFFFF, already_converted=True)