예제 #1
0
def convert_ATvalue(
    byte_string: bytes,
    is_little_endian: bool,
    struct_format: Optional[str] = None
) -> Union[BaseTag, MutableSequence[BaseTag]]:
    """Return a decoded 'AT' value.

    Parameters
    ----------
    byte_string : bytes
        The encoded 'AT' element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str, optional
        Not used.

    Returns
    -------
    BaseTag or MultiValue of BaseTag
        The decoded value(s).
    """
    length = len(byte_string)
    if length == 4:
        return convert_tag(byte_string, is_little_endian)

    # length > 4
    if length % 4 != 0:
        logger.warning("Expected length to be multiple of 4 for VR 'AT', "
                       f"got length {length}")
    return MultiValue(Tag, [
        convert_tag(byte_string, is_little_endian, offset=x)
        for x in range(0, length, 4)
    ])
예제 #2
0
def read_item(fp):
    """Read and return a single Item in the fragmented data stream"""
    try:
        tag = fp.read_tag()
    except EOFError:  # already read delimiter before passing data here, so should just run out
        return None
    if tag == SequenceDelimiterTag:  # No more items, time for sequence to stop reading
        length = fp.read_UL()
        logger.debug("%04x: Sequence Delimiter, length 0x%x",
                     fp.tell() - 8, length)
        if length != 0:
            logger.warning(
                "Expected 0x00000000 after delimiter, found 0x%x, at data position 0x%x",
                length,
                fp.tell() - 4)
        return None
    if tag != ItemTag:
        logger.warning("Expected Item with tag %s at data position 0x%x",
                       ItemTag,
                       fp.tell() - 4)
        length = fp.read_UL()
    else:
        length = fp.read_UL()
        logger.debug("%04x: Item, length 0x%x", fp.tell() - 8, length)

    if length == 0xFFFFFFFF:
        raise ValueError(
            "Encapsulated data fragment had Undefined Length at data position 0x%x"
            % fp.tell() - 4)
    item_data = fp.read(length)
    return item_data
예제 #3
0
def _TM_from_str(value: str) -> TM:
    value = value.rstrip()
    length = len(value)
    if (length < 2 or length > 16) and length != 0:
        logger.warning(
            f"Expected length between 2 and 16, got length {length}")

    return TM(value)
예제 #4
0
def _DT_from_str(value: str) -> DT:
    value = value.rstrip()
    length = len(value)
    if length < 4 or length > 26:
        logger.warning(
            f"Expected length between 4 and 26, got length {length}")

    return DT(value)
예제 #5
0
def read_sequence_item(fp: BinaryIO,
                       is_implicit_VR: bool,
                       is_little_endian: bool,
                       encoding: Union[str, MutableSequence[str]],
                       offset: int = 0) -> Optional[Dataset]:
    """Read and return a single :class:`~pydicom.sequence.Sequence` item, i.e.
    a :class:`~pydicom.dataset.Dataset`.
    """
    seq_item_tell = fp.tell() + offset
    if is_little_endian:
        tag_length_format = "<HHL"
    else:
        tag_length_format = ">HHL"
    try:
        bytes_read = fp.read(8)
        group, element, length = unpack(tag_length_format, bytes_read)
    except BaseException:
        raise IOError(
            f"No tag to read at file position {fp.tell() + offset:X}")

    tag = (group, element)
    if tag == SequenceDelimiterTag:  # No more items, time to stop reading
        logger.debug(f"{fp.tell() - 8 + offset:08x}: End of Sequence")
        if length != 0:
            logger.warning(
                f"Expected 0x00000000 after delimiter, found 0x{length:X}, "
                f"at position 0x{fp.tell() - 4 + offset:X}")
        return None

    if tag != ItemTag:
        logger.warning(
            f"Expected sequence item with tag {ItemTag} at file position "
            f"0x{fp.tell() - 4 + offset:X}")
    else:
        logger.debug(f"{fp.tell() - 4 + offset:08x}: {bytes2hex(bytes_read)}  "
                     "Found Item tag (start of item)")

    if length == 0xFFFFFFFF:
        ds = read_dataset(fp,
                          is_implicit_VR,
                          is_little_endian,
                          bytelength=None,
                          parent_encoding=encoding,
                          at_top_level=False)
        ds.is_undefined_length_sequence_item = True
    else:
        ds = read_dataset(fp,
                          is_implicit_VR,
                          is_little_endian,
                          length,
                          parent_encoding=encoding,
                          at_top_level=False)
        ds.is_undefined_length_sequence_item = False

        logger.debug(f"{fp.tell() + offset:08X}: Finished sequence item")

    ds.seq_item_tell = seq_item_tell
    return ds
예제 #6
0
파일: values.py 프로젝트: willhd/pydicom
def convert_numbers(byte_string, is_little_endian, struct_format):
    """Convert `byte_string` to a value,
       depending on `struct_format`.

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

    Parameters
    ----------
    byte_string : bytes
        The raw byte data to decode.
    is_little_endian : bool
        The encoding of `byte_string`.
    struct_format : str
        The type of data encoded in `byte_string`.

    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:
        logger.warning("Expected length to be even multiple of number size")

    format_string = "%c%u%c" % (endianChar, length // bytes_per_value,
                                struct_format)

    value = unpack(format_string, byte_string)

    # if the number is empty, then return the empty
    # string rather than empty list
    if len(value) == 0:
        return ''
    elif len(value) == 1:
        return value[0]
    else:
        # convert from tuple to a list so can modify if need to
        return list(value)
예제 #7
0
파일: values.py 프로젝트: kayarre/pydicom
def convert_numbers(byte_string, is_little_endian, struct_format):
    """Convert `byte_string` to a value,
       depending on `struct_format`.

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

    Parameters
    ----------
    byte_string : bytes
        The raw byte data to decode.
    is_little_endian : bool
        The encoding of `byte_string`.
    struct_format : str
        The type of data encoded in `byte_string`.

    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:
        logger.warning("Expected length to be even multiple of number size")

    format_string = "%c%u%c" % (endianChar, length // bytes_per_value,
                                struct_format)

    value = unpack(format_string, byte_string)

    # if the number is empty, then return the empty
    # string rather than empty list
    if len(value) == 0:
        return ''
    elif len(value) == 1:
        return value[0]
    else:
        # convert from tuple to a list so can modify if need to
        return list(value)
예제 #8
0
def read_sequence_item(fp,
                       is_implicit_VR,
                       is_little_endian,
                       encoding,
                       offset=0):
    """Read and return a single :class:`~pydicom.sequence.Sequence` item, i.e.
    a :class:`~pydicom.dataset.Dataset`.
    """
    seq_item_tell = fp.tell() + offset
    if is_little_endian:
        tag_length_format = "<HHL"
    else:
        tag_length_format = ">HHL"
    try:
        bytes_read = fp.read(8)
        group, element, length = unpack(tag_length_format, bytes_read)
    except BaseException:
        raise IOError("No tag to read at file position "
                      "{0:05x}".format(fp.tell() + offset))
    tag = (group, element)
    if tag == SequenceDelimiterTag:  # No more items, time to stop reading
        logger.debug("{0:08x}: {1}".format(fp.tell() - 8 + offset,
                                           "End of Sequence"))
        if length != 0:
            logger.warning("Expected 0x00000000 after delimiter, found 0x%x, "
                           "at position 0x%x" %
                           (length, fp.tell() - 4 + offset))
        return None
    if tag != ItemTag:
        logger.warning("Expected sequence item with tag %s at file position "
                       "0x%x" % (ItemTag, fp.tell() - 4 + offset))
    else:
        logger.debug("{0:08x}: {1}  Found Item tag (start of item)".format(
            fp.tell() - 4 + offset, bytes2hex(bytes_read)))
    if length == 0xFFFFFFFF:
        ds = read_dataset(fp,
                          is_implicit_VR,
                          is_little_endian,
                          bytelength=None,
                          parent_encoding=encoding,
                          at_top_level=False)
        ds.is_undefined_length_sequence_item = True
    else:
        ds = read_dataset(fp,
                          is_implicit_VR,
                          is_little_endian,
                          length,
                          parent_encoding=encoding,
                          at_top_level=False)
        ds.is_undefined_length_sequence_item = False
        logger.debug("%08x: Finished sequence item" % (fp.tell() + offset, ))
    ds.seq_item_tell = seq_item_tell
    return ds
def convert_numbers(
        byte_string: bytes, is_little_endian: bool,
        struct_format: str) -> Union[str, int, float, List[Union[int, 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:
        logger.warning("Expected length to be even multiple of number size")

    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)
예제 #10
0
def read_sequence_item(fp, is_implicit_VR, is_little_endian, encoding, offset=0):
    """Read and return a single sequence item, i.e. a Dataset"""
    seq_item_tell = fp.tell() + offset
    if is_little_endian:
        tag_length_format = "<HHL"
    else:
        tag_length_format = ">HHL"
    try:
        bytes_read = fp.read(8)
        group, element, length = unpack(tag_length_format, bytes_read)
    except:
        raise IOError("No tag to read at file position " "{0:05x}".format(fp.tell() + offset))
    tag = (group, element)
    if tag == SequenceDelimiterTag:  # No more items, time to stop reading
        logger.debug("{0:08x}: {1}".format(fp.tell() - 8 + offset, "End of Sequence"))
        if length != 0:
            logger.warning(
                "Expected 0x00000000 after delimiter, found 0x%x, "
                "at position 0x%x" % (length, fp.tell() - 4 + offset)
            )
        return None
    if tag != ItemTag:
        logger.warning(
            "Expected sequence item with tag %s at file position " "0x%x" % (ItemTag, fp.tell() - 4 + offset)
        )
    else:
        logger.debug(
            "{0:08x}: {1}  Found Item tag (start of item)".format(fp.tell() - 4 + offset, bytes2hex(bytes_read))
        )
    if length == 0xFFFFFFFF:
        ds = read_dataset(
            fp, is_implicit_VR, is_little_endian, bytelength=None, parent_encoding=encoding, value_tell_offset=offset
        )
        ds.is_undefined_length_sequence_item = True
    else:
        ds = read_dataset(
            fp, is_implicit_VR, is_little_endian, length, parent_encoding=encoding, value_tell_offset=offset
        )
        ds.is_undefined_length_sequence_item = False
        logger.debug("%08x: Finished sequence item" % (fp.tell() + offset,))
    ds.seq_item_tell = seq_item_tell
    return ds
예제 #11
0
파일: encaps.py 프로젝트: bastula/pydicom
def read_item(fp):
    """Read and return a single Item in the fragmented data stream"""
    try:
        tag = fp.read_tag()
    except EOFError:  # already read delimiter before passing data here, so should just run out
        return None
    if tag == SequenceDelimiterTag:  # No more items, time for sequence to stop reading
        length = fp.read_UL()
        logger.debug("%04x: Sequence Delimiter, length 0x%x", fp.tell() - 8, length)
        if length != 0:
            logger.warning("Expected 0x00000000 after delimiter, found 0x%x, at data position 0x%x", length, fp.tell() - 4)
        return None
    if tag != ItemTag:
        logger.warning("Expected Item with tag %s at data position 0x%x", ItemTag, fp.tell() - 4)
        length = fp.read_UL()
    else:
        length = fp.read_UL()
        logger.debug("%04x: Item, length 0x%x", fp.tell() - 8, length)

    if length == 0xFFFFFFFF:
        raise ValueError("Encapsulated data fragment had Undefined Length at data position 0x%x" % fp.tell() - 4)
    item_data = fp.read(length)
    return item_data
예제 #12
0
    def from_json(cls,
                  dataset_class,
                  tag,
                  vr,
                  value,
                  value_key,
                  bulk_data_uri_handler=None,
                  encodings=None):
        """Creates a DataElement from JSON.

        Parameters
        ----------
        dataset_class: Dataset derived class
            class used to create sequence items
        tag: pydicom.tag.Tag
            data element tag
        vr: str
            data element value representation
        value: list
            data element value(s)
        value_key: Union[str, None]
            key of the data element that contains the value
            (options: ``{"Value", "InlineBinary", "BulkDataURI"}``)
        bulk_data_uri_handler: Union[Callable, None]
            callable that accepts the "BulkDataURI" of the JSON representation
            of a data element and returns the actual value of that data element
            (retrieved via DICOMweb WADO-RS)

        Returns
        -------
        pydicom.dataelem.DataElement

        """
        # TODO: test wado-rs retrieve wrapper
        try:
            vm = dictionary_VM(tag)
        except KeyError:
            # Private tag
            vm = str(len(value))
        if value_key == 'Value':
            if not (isinstance(value, list)):
                fmt = '"{}" of data element "{}" must be a list.'
                raise TypeError(fmt.format(value_key, tag))
        elif value_key in {'InlineBinary', 'BulkDataURI'}:
            if isinstance(value, list):
                fmt = '"{}" of data element "{}" must be a {}.'
                expected_type = ('string' if value_key == 'BulkDataURI' else
                                 'bytes-like object')
                raise TypeError(fmt.format(value_key, tag, expected_type))
        if vr == 'SQ':
            elem_value = []
            for value_item in value:
                ds = dataset_class()
                if value_item:
                    for key, val in value_item.items():
                        if 'vr' not in val:
                            fmt = 'Data element "{}" must have key "vr".'
                            raise KeyError(fmt.format(tag))
                        unique_value_keys = tuple(
                            set(val.keys()) & set(jsonrep.JSON_VALUE_KEYS))
                        if len(unique_value_keys) == 0:
                            logger.debug(
                                'data element has neither key "{}".'.format(
                                    '" nor "'.join(jsonrep.JSON_VALUE_KEYS)))
                            elem = DataElement(tag=tag, value='', VR=vr)
                        else:
                            value_key = unique_value_keys[0]
                            elem = cls.from_json(dataset_class, key, val['vr'],
                                                 val[value_key], value_key)
                        ds.add(elem)
                elem_value.append(ds)
        elif vr == 'PN':
            # Special case, see DICOM Part 18 Annex F2.2
            elem_value = []
            for v in value:
                if not isinstance(v, dict):
                    # Some DICOMweb services get this wrong, so we
                    # workaround the issue and warn the user
                    # rather than raising an error.
                    logger.error(
                        'value of data element "{}" with VR Person Name (PN) '
                        'is not formatted correctly'.format(tag))
                    elem_value.append(v)
                else:
                    elem_value.extend(list(v.values()))
            if vm == '1':
                try:
                    elem_value = elem_value[0]
                except IndexError:
                    elem_value = ''
        else:
            if vm == '1':
                if value_key == 'InlineBinary':
                    elem_value = base64.b64decode(value)
                elif value_key == 'BulkDataURI':
                    if bulk_data_uri_handler is None:
                        logger.warning(
                            'no bulk data URI handler provided for retrieval '
                            'of value of data element "{}"'.format(tag))
                        elem_value = b''
                    else:
                        elem_value = bulk_data_uri_handler(value)
                else:
                    if value:
                        elem_value = value[0]
                    else:
                        elem_value = value
            else:
                elem_value = value
        if elem_value is None:
            logger.warning('missing value for data element "{}"'.format(tag))
            elem_value = ''

        elem_value = jsonrep.convert_to_python_number(elem_value, vr)

        try:
            if compat.in_py2 and vr == "PN":
                elem_value = PersonNameUnicode(elem_value, 'UTF8')
            return DataElement(tag=tag, value=elem_value, VR=vr)
        except Exception:
            raise ValueError(
                'Data element "{}" could not be loaded from JSON: {}'.format(
                    tag, elem_value))