Example #1
0
    def test_charset_patient_names(self, filename, patient_name):
        """Test patient names are correctly decoded and encoded."""
        # check that patient names are correctly read
        file_path = get_charset_files(filename + '.dcm')[0]
        ds = dcmread(file_path)
        ds.decode()
        assert patient_name == ds.PatientName

        # check that patient names are correctly written back
        fp = DicomBytesIO()
        fp.is_implicit_VR = False
        fp.is_little_endian = True
        ds.save_as(fp, write_like_original=False)
        fp.seek(0)
        ds = dcmread(fp)
        assert patient_name == ds.PatientName

        # check that patient names are correctly written back
        # without original byte string (PersonName3 only)
        if hasattr(ds.PatientName, 'original_string'):
            ds.PatientName.original_string = None
            fp = DicomBytesIO()
            fp.is_implicit_VR = False
            fp.is_little_endian = True
            ds.save_as(fp, write_like_original=False)
            fp.seek(0)
            ds = dcmread(fp)
            assert patient_name == ds.PatientName
Example #2
0
    def test_charset_patient_names(self, filename, patient_name):
        """Test patient names are correctly decoded and encoded."""
        # check that patient names are correctly read
        file_path = get_charset_files(filename + '.dcm')[0]
        ds = dcmread(file_path)
        ds.decode()
        assert patient_name == ds.PatientName

        # check that patient names are correctly written back
        fp = DicomBytesIO()
        fp.is_implicit_VR = False
        fp.is_little_endian = True
        ds.save_as(fp, write_like_original=False)
        fp.seek(0)
        ds = dcmread(fp)
        assert patient_name == ds.PatientName

        # check that patient names are correctly written back
        # without original byte string (PersonName3 only)
        if hasattr(ds.PatientName, 'original_string'):
            ds.PatientName.original_string = None
            fp = DicomBytesIO()
            fp.is_implicit_VR = False
            fp.is_little_endian = True
            ds.save_as(fp, write_like_original=False)
            fp.seek(0)
            ds = dcmread(fp)
            assert patient_name == ds.PatientName
Example #3
0
    def test_japanese_multi_byte_personname(self):
        """Test japanese person name which has multi byte strings are
        correctly encoded."""
        file_path = get_charset_files('chrH32.dcm')[0]
        ds = dcmread(file_path)
        ds.decode()

        if hasattr(ds.PatientName, 'original_string'):
            original_string = ds.PatientName.original_string
            ds.PatientName.original_string = None
            fp = DicomBytesIO()
            fp.is_implicit_VR = False
            fp.is_little_endian = True
            ds.save_as(fp, write_like_original=False)
            fp.seek(0)
            ds_out = dcmread(fp)
            assert original_string == ds_out.PatientName.original_string

        japanese_pn = PersonName(u"Mori^Ogai=森^鷗外=もり^おうがい")
        pyencs = pydicom.charset.convert_encodings(
            ["ISO 2022 IR 6", "ISO 2022 IR 87", "ISO 2022 IR 159"])
        actual_encoded = bytes(japanese_pn.encode(pyencs))
        expect_encoded = (
            b"\x4d\x6f\x72\x69\x5e\x4f\x67\x61\x69\x3d\x1b\x24\x42\x3f"
            b"\x39\x1b\x28\x42\x5e\x1b\x24\x28\x44\x6c\x3f\x1b\x24\x42"
            b"\x33\x30\x1b\x28\x42\x3d\x1b\x24\x42\x24\x62\x24\x6a\x1b"
            b"\x28\x42\x5e\x1b\x24\x42\x24\x2a\x24\x26\x24\x2c\x24\x24"
            b"\x1b\x28\x42")
        assert expect_encoded == actual_encoded
Example #4
0
def encode_element(elem, is_implicit_vr=True, is_little_endian=True):
    """Encode a pydicom DataElement `elem` to a byte stream.

    The default is to encode the element as implicit VR little endian.

    Parameters
    ----------
    elem : pydicom.dataelem.DataElement
        The element to encode
    is_implicit_vr : bool, optional
        The element encoding scheme the element will be encoded with, default
        is True.
    is_little_endian : bool, optional
        The byte ordering the element will be encoded in, default is True.

    Returns
    -------
    bytes
        The encoded element.
    """
    fp = DicomBytesIO()
    fp.is_implicit_VR = is_implicit_vr
    fp.is_little_endian = is_little_endian
    write_data_element(fp, elem)
    bytestring = fp.parent.getvalue()
    fp.close()

    return bytestring
Example #5
0
def encode(ds, is_implicit_VR, is_little_endian):
    """
    Given a pydicom Dataset, encode it to a byte stream
    
    Parameters
    ----------
    ds - pydicom.dataset.Dataset
        The dataset to encode
    is_implicit_VR - bool
        Transfer syntax implicit/explicit VR
    is_little_endian - bool
        Transfer syntax byte ordering
    
    Returns
    -------
    bytes or None
        The encoded dataset (if successful), None if encoding failed.
    """
    f = DicomBytesIO()
    f.is_implicit_VR = is_implicit_VR
    f.is_little_endian = is_little_endian
    try:
        write_dataset(f, ds)
    except Exception as e:
        logger.error("pydicom.write_dataset() failed:")
        logger.error(e)
        f.close()
        return None
    
    rawstr = f.parent.getvalue()
    f.close()
    return rawstr
Example #6
0
def test_deferred_data_element_deprecated():
    """Test the deprecation warning is working"""
    fp = DicomBytesIO()
    fp.is_little_endian = True
    fp.is_implicit_VR = True
    with pytest.deprecated_call():
        elem = DeferredDataElement(0x00000000, 'UL', fp, 0, 0, 4)
Example #7
0
def test_deferred_data_element_deprecated():
    """Test the deprecation warning is working"""
    fp = DicomBytesIO()
    fp.is_little_endian = True
    fp.is_implicit_VR = True
    with pytest.deprecated_call():
        elem = DeferredDataElement(0x00000000, 'UL', fp, 0, 0, 4)
Example #8
0
def encode_element(elem, is_implicit_vr=True, is_little_endian=True):
    """Encode a *pydicom* :class:`~pydicom.dataelem.DataElement` `elem`.

    .. deprecated:: 1.5

        Will be removed in version 2.0, use *pydicom* instead.

    Parameters
    ----------
    elem : pydicom.dataelem.DataElement
        The element to encode.
    is_implicit_vr : bool, optional
        The element encoding scheme the element will be encoded with, ``True``
        for implicit VR (default), ``False`` for explicit VR.
    is_little_endian : bool, optional
        The byte ordering the element will be encoded in, ``True`` for little
        endian (default), ``False`` for big endian.

    Returns
    -------
    bytes
        The encoded element.
    """
    fp = DicomBytesIO()
    fp.is_implicit_VR = is_implicit_vr
    fp.is_little_endian = is_little_endian
    write_data_element(fp, elem)
    bytestring = fp.parent.getvalue()
    fp.close()

    return bytestring
Example #9
0
def encode(ds, is_implicit_vr, is_little_endian):
    """Encode a pydicom Dataset `ds` to a byte stream.

    Parameters
    ----------
    ds : pydicom.dataset.Dataset
        The dataset to encode
    is_implicit_vr : bool
        The element encoding scheme the dataset will be encoded with.
    is_little_endian : bool
        The byte ordering the dataset will be encoded in.

    Returns
    -------
    bytes or None
        The encoded dataset (if successful), None if the encoding failed.
    """
    # pylint: disable=broad-except
    fp = DicomBytesIO()
    fp.is_implicit_VR = is_implicit_vr
    fp.is_little_endian = is_little_endian
    try:
        write_dataset(fp, ds)
    except Exception as ex:
        LOGGER.error("pydicom.write_dataset() failed:")
        LOGGER.error(ex)
        fp.close()
        return None

    bytestring = fp.parent.getvalue()
    fp.close()

    return bytestring
Example #10
0
def encode(ds, is_implicit_VR, is_little_endian):
    f = DicomBytesIO()
    f.is_implicit_VR = is_implicit_VR
    f.is_little_endian = is_little_endian
    write_dataset(f, ds)
    rawstr = f.parent.getvalue()
    f.close()
    return rawstr
Example #11
0
def encode_element(el, is_implicit_VR, is_little_endian):
    f = DicomBytesIO()
    f.is_implicit_VR = is_implicit_VR
    f.is_little_endian = is_little_endian
    write_data_element(f, el)
    rawstr = f.parent.getvalue()
    f.close()
    return rawstr
Example #12
0
 def test_lut_descriptor_empty(self):
     """Regression test for #1049: LUT empty raises."""
     bs = DicomBytesIO(b'\x28\x00\x01\x11\x53\x53\x00\x00')
     bs.is_little_endian = True
     bs.is_implicit_VR = False
     ds = dcmread(bs, force=True)
     elem = ds[0x00281101]
     assert elem.value is None
     assert elem.VR == 'SS'
Example #13
0
 def test_lut_descriptor_singleton(self):
     """Test LUT Descriptor with VM = 1"""
     bs = DicomBytesIO(b'\x28\x00\x01\x11\x53\x53\x02\x00\x00\xf5')
     bs.is_little_endian = True
     bs.is_implicit_VR = False
     ds = dcmread(bs, force=True)
     elem = ds[0x00281101]
     # No conversion to US if not a triplet
     assert elem.value == -2816
     assert elem.VR == 'SS'
Example #14
0
def encode(ds: Dataset,
           is_implicit_vr: bool,
           is_little_endian: bool,
           deflated: bool = False) -> Optional[bytes]:
    """Encode a *pydicom* :class:`~pydicom.dataset.Dataset` `ds`.

    .. versionchanged:: 1.5

        Added `deflated` keyword parameter

    Parameters
    ----------
    ds : pydicom.dataset.Dataset
        The dataset to encode
    is_implicit_vr : bool
        The element encoding scheme the dataset will be encoded with, ``True``
        for implicit VR, ``False`` for explicit VR.
    is_little_endian : bool
        The byte ordering the dataset will be encoded in, ``True`` for little
        endian, ``False`` for big endian.
    deflated : bool, optional
        ``True`` if the dataset is to be encoded using *Deflated Explicit VR
        Little Endian* transfer syntax (default ``False``).

    Returns
    -------
    bytes or None
        The encoded dataset as :class:`bytes` (if successful) or ``None`` if
        the encoding failed.
    """
    # pylint: disable=broad-except
    fp = DicomBytesIO()
    fp.is_implicit_VR = is_implicit_vr
    fp.is_little_endian = is_little_endian
    try:
        write_dataset(fp, ds)
    except Exception as exc:
        LOGGER.error("pydicom.write_dataset() failed:")
        LOGGER.exception(exc)
        fp.close()
        return None

    bytestring: bytes = fp.parent.getvalue()  # type: ignore
    fp.close()

    if deflated:
        # Compress the encoded dataset
        compressor = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
                                      zlib.DEFLATED, -zlib.MAX_WBITS)
        bytestring = compressor.compress(bytestring)
        bytestring += compressor.flush()
        bytestring += b'\x00' if len(bytestring) % 2 else b''

    return bytestring
Example #15
0
def decode(bytestring, is_implicit_vr, is_little_endian, deflated=False):
    """Decode `bytestring` to a *pydicom* :class:`~pydicom.dataset.Dataset`.

    .. versionchanged:: 1.5

        Added `deflated` keyword parameter

    Parameters
    ----------
    byestring : io.BytesIO
        The encoded dataset in the DIMSE Message sent from the peer AE.
    is_implicit_vr : bool
        The dataset is encoded as implicit (``True``) or explicit VR
        (``False``).
    is_little_endian : bool
        The byte ordering of the encoded dataset, ``True`` for little endian,
        ``False`` for big endian.
    deflated : bool, optional
        ``True`` if the dataset has been encoded using *Deflated Explicit VR
        Little Endian* transfer syntax (default ``False``).

    Returns
    -------
    pydicom.dataset.Dataset
        The decoded dataset.
    """
    ## Logging
    transfer_syntax = ''
    if deflated:
        transfer_syntax = "Deflated "

    transfer_syntax += "Little Endian" if is_little_endian else "Big Endian"
    if is_implicit_vr:
        transfer_syntax += " Implicit"
    else:
        transfer_syntax += " Explicit"

    LOGGER.debug('pydicom.read_dataset() TransferSyntax="%s"', transfer_syntax)

    # Rewind to the start of the stream
    bytestring.seek(0)

    if deflated:
        # Decompress the dataset
        bytestring = DicomBytesIO(
            zlib.decompress(bytestring.getvalue(), -zlib.MAX_WBITS))
        bytestring.is_implicit_VR = is_implicit_vr
        bytestring.is_little_endian = is_little_endian

    # Decode the dataset
    return read_dataset(bytestring, is_implicit_vr, is_little_endian)
Example #16
0
    def test_empty_sequence_is_handled_as_array(self):
        ds = Dataset()
        ds.AcquisitionContextSequence = []
        elem = ds['AcquisitionContextSequence']
        assert bool(elem.value) is False
        assert 0 == elem.VM
        assert elem.value == []

        fp = DicomBytesIO()
        fp.is_little_endian = True
        fp.is_implicit_VR = True
        filewriter.write_dataset(fp, ds)
        ds_read = dcmread(fp, force=True)
        elem = ds_read['AcquisitionContextSequence']
        assert 0 == elem.VM
        assert elem.value == []
Example #17
0
    def test_japanese_multi_byte_personname(self):
        """Test japanese person name which has multi byte strings are
        correctly encoded."""
        file_path = get_charset_files('chrH32.dcm')[0]
        ds = dcmread(file_path)
        ds.decode()

        if hasattr(ds.PatientName, 'original_string'):
            original_string = ds.PatientName.original_string
            ds.PatientName.original_string = None
            fp = DicomBytesIO()
            fp.is_implicit_VR = False
            fp.is_little_endian = True
            ds.save_as(fp, write_like_original=False)
            fp.seek(0)
            ds_out = dcmread(fp)
            assert original_string == ds_out.PatientName.original_string
Example #18
0
    def test_lut_descriptor(self):
        """Regression test for #942: incorrect first value"""
        prefixes = [
            b'\x28\x00\x01\x11', b'\x28\x00\x02\x11', b'\x28\x00\x03\x11',
            b'\x28\x00\x02\x30'
        ]
        suffix = b'\x53\x53\x06\x00\x00\xf5\x00\xf8\x10\x00'

        for raw_tag in prefixes:
            tag = unpack('<2H', raw_tag)
            bs = DicomBytesIO(raw_tag + suffix)
            bs.is_little_endian = True
            bs.is_implicit_VR = False

            ds = dcmread(bs, force=True)
            elem = ds[tag]
            assert elem.VR == 'SS'
            assert elem.value == [62720, -2048, 16]
Example #19
0
    def encode_element(elem, is_implicit_VR=True, is_little_endian=True):
        """Return the encoded `elem`.

        Parameters
        ----------
        elem : pydicom.dataelem.DataElement
            The element to encode
        is_implicit_VR : bool
            Encode using implicit VR, default True
        is_little_endian : bool
            Encode using little endian, default True

        Returns
        -------
        str or bytes
            The encoded element as str (python2) or bytes (python3)
        """
        fp = DicomBytesIO()
        fp.is_implicit_VR = is_implicit_VR
        fp.is_little_endian = is_little_endian
        write_data_element(fp, elem)
        byte_string = fp.parent.getvalue()
        fp.close()
        return byte_string
Example #20
0
def encode(ds, is_implicit_vr, is_little_endian):
    """Encode a *pydicom* :class:`~pydicom.dataset.Dataset` `ds`.

    Parameters
    ----------
    ds : pydicom.dataset.Dataset
        The dataset to encode
    is_implicit_vr : bool
        The element encoding scheme the dataset will be encoded with, ``True``
        for implicit VR, ``False`` for explicit VR.
    is_little_endian : bool
        The byte ordering the dataset will be encoded in, ``True`` for little
        endian, ``False`` for big endian.

    Returns
    -------
    bytes or None
        The encoded dataset as :class:`bytes` (if successful) or ``None`` if
        the encoding failed.
    """
    # pylint: disable=broad-except
    fp = DicomBytesIO()
    fp.is_implicit_VR = is_implicit_vr
    fp.is_little_endian = is_little_endian
    try:
        write_dataset(fp, ds)
    except Exception as exc:
        LOGGER.error("pydicom.write_dataset() failed:")
        LOGGER.exception(exc)
        fp.close()
        return None

    bytestring = fp.parent.getvalue()
    fp.close()

    return bytestring
Example #21
0
def write_data_element(fp, data_element, encodings=None):
    """Write the data_element to file fp according to
    dicom media storage rules.
    """
    # Write element's tag
    fp.write_tag(data_element.tag)

    # If explicit VR, write the VR
    VR = data_element.VR
    if not fp.is_implicit_VR:
        if len(VR) != 2:
            msg = ("Cannot write ambiguous VR of '{}' for data element with "
                   "tag {}.\nSet the correct VR before writing, or use an "
                   "implicit VR transfer syntax".format(
                       VR, repr(data_element.tag)))
            raise ValueError(msg)
        if not in_py2:
            fp.write(bytes(VR, default_encoding))
        else:
            fp.write(VR)
        if VR in extra_length_VRs:
            fp.write_US(0)  # reserved 2 bytes

    # write into a buffer to avoid seeking back which can be expansive
    buffer = DicomBytesIO()
    buffer.is_little_endian = fp.is_little_endian
    buffer.is_implicit_VR = fp.is_implicit_VR

    if data_element.is_raw:
        # raw data element values can be written as they are
        buffer.write(data_element.value)
        is_undefined_length = data_element.length == 0xFFFFFFFF
    else:
        if VR not in writers:
            raise NotImplementedError(
                "write_data_element: unknown Value Representation "
                "'{0}'".format(VR))

        encodings = encodings or [default_encoding]
        encodings = convert_encodings(encodings)
        writer_function, writer_param = writers[VR]
        is_undefined_length = data_element.is_undefined_length
        if VR in text_VRs or VR in ('PN', 'SQ'):
            writer_function(buffer, data_element, encodings=encodings)
        else:
            # Many numeric types use the same writer but with numeric format
            # parameter
            if writer_param is not None:
                writer_function(buffer, data_element, writer_param)
            else:
                writer_function(buffer, data_element)

    # valid pixel data with undefined length shall contain encapsulated
    # data, e.g. sequence items - raise ValueError otherwise (see #238)
    if is_undefined_length and data_element.tag == 0x7fe00010:
        val = data_element.value
        if (fp.is_little_endian and not val.startswith(b'\xfe\xff\x00\xe0')
                or not fp.is_little_endian
                and not val.startswith(b'\xff\xfe\xe0\x00')):
            raise ValueError('Pixel Data with undefined length must '
                             'start with an item tag')

    value_length = buffer.tell()
    if (not fp.is_implicit_VR and VR not in extra_length_VRs
            and not is_undefined_length):
        fp.write_US(value_length)  # Explicit VR length field is only 2 bytes
    else:
        # write the proper length of the data_element in the length slot,
        # unless is SQ with undefined length.
        fp.write_UL(0xFFFFFFFF if is_undefined_length else value_length)

    fp.write(buffer.getvalue())
    if is_undefined_length:
        fp.write_tag(SequenceDelimiterTag)
        fp.write_UL(0)  # 4-byte 'length' of delimiter data item
Example #22
0
def write_data_element(
        fp: DicomIO,
        elem: Union[DataElement, RawDataElement],
        encodings: Optional[Union[str, List[str]]] = None) -> None:
    """Write the data_element to file fp according to
    dicom media storage rules.
    """
    # Write element's tag
    fp.write_tag(elem.tag)

    # write into a buffer to avoid seeking back which can be expansive
    buffer = DicomBytesIO()
    buffer.is_little_endian = fp.is_little_endian
    buffer.is_implicit_VR = fp.is_implicit_VR

    VR: Optional[str] = elem.VR
    if not fp.is_implicit_VR and VR and len(VR) != 2:
        msg = (f"Cannot write ambiguous VR of '{VR}' for data element with "
               f"tag {repr(elem.tag)}.\nSet the correct VR before "
               f"writing, or use an implicit VR transfer syntax")
        raise ValueError(msg)

    if elem.is_raw:
        elem = cast(RawDataElement, elem)
        # raw data element values can be written as they are
        buffer.write(cast(bytes, elem.value))
        is_undefined_length = elem.length == 0xFFFFFFFF
    else:
        elem = cast(DataElement, elem)
        if VR not in writers:
            raise NotImplementedError(
                f"write_data_element: unknown Value Representation '{VR}'")

        encodings = encodings or [default_encoding]
        encodings = convert_encodings(encodings)
        fn, param = writers[VR]
        is_undefined_length = elem.is_undefined_length
        if not elem.is_empty:
            if VR in text_VRs or VR in ('PN', 'SQ'):
                fn(buffer, elem, encodings=encodings)  # type: ignore[operator]
            else:
                # Many numeric types use the same writer but with
                # numeric format parameter
                if param is not None:
                    fn(buffer, elem, param)  # type: ignore[operator]
                else:
                    fn(buffer, elem)  # type: ignore[operator]

    # valid pixel data with undefined length shall contain encapsulated
    # data, e.g. sequence items - raise ValueError otherwise (see #238)
    if is_undefined_length and elem.tag == 0x7fe00010:
        encap_item = b'\xfe\xff\x00\xe0'
        if not fp.is_little_endian:
            # Non-conformant endianness
            encap_item = b'\xff\xfe\xe0\x00'
        if not cast(bytes, elem.value).startswith(encap_item):
            raise ValueError(
                "(7FE0,0010) Pixel Data has an undefined length indicating "
                "that it's compressed, but the data isn't encapsulated as "
                "required. See pydicom.encaps.encapsulate() for more "
                "information")

    value_length = buffer.tell()
    if (not fp.is_implicit_VR and VR not in extra_length_VRs
            and not is_undefined_length and value_length > 0xffff):
        # see PS 3.5, section 6.2.2 for handling of this case
        msg = (
            f"The value for the data element {elem.tag} exceeds the "
            f"size of 64 kByte and cannot be written in an explicit transfer "
            f"syntax. The data element VR is changed from '{VR}' to 'UN' "
            f"to allow saving the data.")
        warnings.warn(msg)
        VR = 'UN'

    # write the VR for explicit transfer syntax
    if not fp.is_implicit_VR:
        VR = cast(str, VR)
        fp.write(bytes(VR, default_encoding))

        if VR in extra_length_VRs:
            fp.write_US(0)  # reserved 2 bytes

    if (not fp.is_implicit_VR and VR not in extra_length_VRs
            and not is_undefined_length):
        fp.write_US(value_length)  # Explicit VR length field is 2 bytes
    else:
        # write the proper length of the data_element in the length slot,
        # unless is SQ with undefined length.
        fp.write_UL(0xFFFFFFFF if is_undefined_length else value_length)

    fp.write(buffer.getvalue())
    if is_undefined_length:
        fp.write_tag(SequenceDelimiterTag)
        fp.write_UL(0)  # 4-byte 'length' of delimiter data item
Example #23
0
def write_data_element(fp, data_element, encodings=None):
    """Write the data_element to file fp according to
    dicom media storage rules.
    """
    # Write element's tag
    fp.write_tag(data_element.tag)

    # write into a buffer to avoid seeking back which can be expansive
    buffer = DicomBytesIO()
    buffer.is_little_endian = fp.is_little_endian
    buffer.is_implicit_VR = fp.is_implicit_VR

    VR = data_element.VR
    if not fp.is_implicit_VR and len(VR) != 2:
        msg = ("Cannot write ambiguous VR of '{}' for data element with "
               "tag {}.\nSet the correct VR before writing, or use an "
               "implicit VR transfer syntax".format(VR,
                                                    repr(data_element.tag)))
        raise ValueError(msg)

    if data_element.is_raw:
        # raw data element values can be written as they are
        buffer.write(data_element.value)
        is_undefined_length = data_element.length == 0xFFFFFFFF
    else:
        if VR not in writers:
            raise NotImplementedError(
                "write_data_element: unknown Value Representation "
                "'{0}'".format(VR))

        encodings = encodings or [default_encoding]
        encodings = convert_encodings(encodings)
        writer_function, writer_param = writers[VR]
        is_undefined_length = data_element.is_undefined_length
        if not data_element.is_empty:
            if VR in text_VRs or VR in ('PN', 'SQ'):
                writer_function(buffer, data_element, encodings=encodings)
            else:
                # Many numeric types use the same writer but with
                # numeric format parameter
                if writer_param is not None:
                    writer_function(buffer, data_element, writer_param)
                else:
                    writer_function(buffer, data_element)

    # valid pixel data with undefined length shall contain encapsulated
    # data, e.g. sequence items - raise ValueError otherwise (see #238)
    if is_undefined_length and data_element.tag == 0x7fe00010:
        encap_item = b'\xfe\xff\x00\xe0'
        if not fp.is_little_endian:
            # Non-conformant endianness
            encap_item = b'\xff\xfe\xe0\x00'
        if not data_element.value.startswith(encap_item):
            raise ValueError(
                "(7FE0,0010) Pixel Data has an undefined length indicating "
                "that it's compressed, but the data isn't encapsulated as "
                "required. See pydicom.encaps.encapsulate() for more "
                "information")

    value_length = buffer.tell()
    if (not fp.is_implicit_VR and VR not in extra_length_VRs
            and not is_undefined_length and value_length > 0xffff):
        # see PS 3.5, section 6.2.2 for handling of this case
        msg = ('The value for the data element {} exceeds the size '
               'of 64 kByte and cannot be written in an explicit transfer '
               'syntax. The data element VR is changed from "{}" to "UN" '
               'to allow saving the data.'.format(data_element.tag, VR))
        warnings.warn(msg)
        VR = 'UN'

    # write the VR for explicit transfer syntax
    if not fp.is_implicit_VR:
        if not in_py2:
            fp.write(bytes(VR, default_encoding))
        else:
            fp.write(VR)
        if VR in extra_length_VRs:
            fp.write_US(0)  # reserved 2 bytes

    if (not fp.is_implicit_VR and VR not in extra_length_VRs
            and not is_undefined_length):
        fp.write_US(value_length)  # Explicit VR length field is 2 bytes
    else:
        # write the proper length of the data_element in the length slot,
        # unless is SQ with undefined length.
        fp.write_UL(0xFFFFFFFF if is_undefined_length else value_length)

    fp.write(buffer.getvalue())
    if is_undefined_length:
        fp.write_tag(SequenceDelimiterTag)
        fp.write_UL(0)  # 4-byte 'length' of delimiter data item
Example #24
0
def write_file_meta_info(fp, file_meta, enforce_standard=True):
    """Write the File Meta Information elements in `file_meta` to `fp`.

    If `enforce_standard` is True then the file-like `fp` should be positioned
    past the 128 byte preamble + 4 byte prefix (which should already have been
    written).

    DICOM File Meta Information Group Elements
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    From the DICOM standard, Part 10 Section 7.1, any DICOM file shall contain
    a 128-byte preamble, a 4-byte DICOM prefix 'DICM' and (at a minimum) the
    following Type 1 DICOM Elements (from Table 7.1-1):
        * (0002,0000) FileMetaInformationGroupLength, UL, 4
        * (0002,0001) FileMetaInformationVersion, OB, 2
        * (0002,0002) MediaStorageSOPClassUID, UI, N
        * (0002,0003) MediaStorageSOPInstanceUID, UI, N
        * (0002,0010) TransferSyntaxUID, UI, N
        * (0002,0012) ImplementationClassUID, UI, N

    If `enforce_standard` is True then (0002,0000) will be added/updated,
    (0002,0001) and (0002,0012) will be added if not already present and the
    other required elements will be checked to see if they exist. If
    `enforce_standard` is False then `file_meta` will be written as is after
    minimal validation checking.

    The following Type 3/1C Elements may also be present:
        * (0002,0013) ImplementationVersionName, SH, N
        * (0002,0016) SourceApplicationEntityTitle, AE, N
        * (0002,0017) SendingApplicationEntityTitle, AE, N
        * (0002,0018) ReceivingApplicationEntityTitle, AE, N
        * (0002,0100) PrivateInformationCreatorUID, UI, N
        * (0002,0102) PrivateInformation, OB, N

    If `enforce_standard` is True then (0002,0013) will be added/updated.

    Encoding
    ~~~~~~~~
    The encoding of the File Meta Information shall be Explicit VR Little
    Endian

    Parameters
    ----------
    fp : file-like
        The file-like to write the File Meta Information to.
    file_meta : pydicom.dataset.Dataset
        The File Meta Information DataElements.
    enforce_standard : bool
        If False, then only the File Meta Information elements already in
        `file_meta` will be written to `fp`. If True (default) then a DICOM
        Standards conformant File Meta will be written to `fp`.

    Raises
    ------
    ValueError
        If `enforce_standard` is True and any of the required File Meta
        Information elements are missing from `file_meta`, with the
        exception of (0002,0000), (0002,0001) and (0002,0012).
    ValueError
        If any non-Group 2 Elements are present in `file_meta`.
    """
    validate_file_meta(file_meta, enforce_standard)

    if enforce_standard and 'FileMetaInformationGroupLength' not in file_meta:
        # Will be updated with the actual length later
        file_meta.FileMetaInformationGroupLength = 0

    # Write the File Meta Information Group elements
    # first write into a buffer to avoid seeking back, that can be
    # expansive and is not allowed if writing into a zip file
    buffer = DicomBytesIO()
    buffer.is_little_endian = True
    buffer.is_implicit_VR = False
    write_dataset(buffer, file_meta)

    # If FileMetaInformationGroupLength is present it will be the first written
    #   element and we must update its value to the correct length.
    if 'FileMetaInformationGroupLength' in file_meta:
        # Update the FileMetaInformationGroupLength value, which is the number
        #   of bytes from the end of the FileMetaInformationGroupLength element
        #   to the end of all the File Meta Information elements.
        # FileMetaInformationGroupLength has a VR of 'UL' and so has a value
        #   that is 4 bytes fixed. The total length of when encoded as
        #   Explicit VR must therefore be 12 bytes.
        file_meta.FileMetaInformationGroupLength = buffer.tell() - 12
        buffer.seek(0)
        write_data_element(buffer, file_meta[0x00020000])

    fp.write(buffer.getvalue())
Example #25
0
def write_file_meta_info(fp, file_meta, enforce_standard=True):
    """Write the File Meta Information elements in `file_meta` to `fp`.

    If `enforce_standard` is ``True`` then the file-like `fp` should be
    positioned past the 128 byte preamble + 4 byte prefix (which should
    already have been written).

    **DICOM File Meta Information Group Elements**

    From the DICOM standard, Part 10,
    :dcm:`Section 7.1<part10/chapter_7.html#sect_7.1>`,  any DICOM file shall
    contain a 128-byte preamble, a 4-byte DICOM prefix 'DICM' and (at a
    minimum) the following Type 1 DICOM Elements (from
    :dcm:`Table 7.1-1<part10/chapter_7.html#table_7.1-1>`):

    * (0002,0000) *File Meta Information Group Length*, UL, 4
    * (0002,0001) *File Meta Information Version*, OB, 2
    * (0002,0002) *Media Storage SOP Class UID*, UI, N
    * (0002,0003) *Media Storage SOP Instance UID*, UI, N
    * (0002,0010) *Transfer Syntax UID*, UI, N
    * (0002,0012) *Implementation Class UID*, UI, N

    If `enforce_standard` is ``True`` then (0002,0000) will be added/updated,
    (0002,0001) and (0002,0012) will be added if not already present and the
    other required elements will be checked to see if they exist. If
    `enforce_standard` is ``False`` then `file_meta` will be written as is
    after minimal validation checking.

    The following Type 3/1C Elements may also be present:

    * (0002,0013) *Implementation Version Name*, SH, N
    * (0002,0016) *Source Application Entity Title*, AE, N
    * (0002,0017) *Sending Application Entity Title*, AE, N
    * (0002,0018) *Receiving Application Entity Title*, AE, N
    * (0002,0102) *Private Information*, OB, N
    * (0002,0100) *Private Information Creator UID*, UI, N

    If `enforce_standard` is ``True`` then (0002,0013) will be added/updated.

    *Encoding*

    The encoding of the *File Meta Information* shall be *Explicit VR Little
    Endian*.

    Parameters
    ----------
    fp : file-like
        The file-like to write the File Meta Information to.
    file_meta : pydicom.dataset.Dataset
        The File Meta Information elements.
    enforce_standard : bool
        If ``False``, then only the *File Meta Information* elements already in
        `file_meta` will be written to `fp`. If ``True`` (default) then a DICOM
        Standards conformant File Meta will be written to `fp`.

    Raises
    ------
    ValueError
        If `enforce_standard` is ``True`` and any of the required *File Meta
        Information* elements are missing from `file_meta`, with the
        exception of (0002,0000), (0002,0001) and (0002,0012).
    ValueError
        If any non-Group 2 Elements are present in `file_meta`.
    """
    validate_file_meta(file_meta, enforce_standard)

    if enforce_standard and 'FileMetaInformationGroupLength' not in file_meta:
        # Will be updated with the actual length later
        file_meta.FileMetaInformationGroupLength = 0

    # Write the File Meta Information Group elements
    # first write into a buffer to avoid seeking back, that can be
    # expansive and is not allowed if writing into a zip file
    buffer = DicomBytesIO()
    buffer.is_little_endian = True
    buffer.is_implicit_VR = False
    write_dataset(buffer, file_meta)

    # If FileMetaInformationGroupLength is present it will be the first written
    #   element and we must update its value to the correct length.
    if 'FileMetaInformationGroupLength' in file_meta:
        # Update the FileMetaInformationGroupLength value, which is the number
        #   of bytes from the end of the FileMetaInformationGroupLength element
        #   to the end of all the File Meta Information elements.
        # FileMetaInformationGroupLength has a VR of 'UL' and so has a value
        #   that is 4 bytes fixed. The total length of when encoded as
        #   Explicit VR must therefore be 12 bytes.
        file_meta.FileMetaInformationGroupLength = buffer.tell() - 12
        buffer.seek(0)
        write_data_element(buffer, file_meta[0x00020000])

    fp.write(buffer.getvalue())
Example #26
0
def write_data_element(fp, data_element, encodings=None):
    """Write the data_element to file fp according to
    dicom media storage rules.
    """
    # Write element's tag
    fp.write_tag(data_element.tag)

    # If explicit VR, write the VR
    VR = data_element.VR
    if not fp.is_implicit_VR:
        if len(VR) != 2:
            msg = ("Cannot write ambiguous VR of '{}' for data element with "
                   "tag {}.\nSet the correct VR before writing, or use an "
                   "implicit VR transfer syntax".format(
                       VR, repr(data_element.tag)))
            raise ValueError(msg)
        if not in_py2:
            fp.write(bytes(VR, default_encoding))
        else:
            fp.write(VR)
        if VR in extra_length_VRs:
            fp.write_US(0)  # reserved 2 bytes

    # write into a buffer to avoid seeking back which can be expansive
    buffer = DicomBytesIO()
    buffer.is_little_endian = fp.is_little_endian
    buffer.is_implicit_VR = fp.is_implicit_VR

    if data_element.is_raw:
        # raw data element values can be written as they are
        buffer.write(data_element.value)
        is_undefined_length = data_element.length == 0xFFFFFFFF
    else:
        if VR not in writers:
            raise NotImplementedError(
                "write_data_element: unknown Value Representation "
                "'{0}'".format(VR))

        encodings = encodings or [default_encoding]
        encodings = convert_encodings(encodings)
        writer_function, writer_param = writers[VR]
        is_undefined_length = data_element.is_undefined_length
        if VR in text_VRs or VR in ('PN', 'SQ'):
            writer_function(buffer, data_element, encodings=encodings)
        else:
            # Many numeric types use the same writer but with numeric format
            # parameter
            if writer_param is not None:
                writer_function(buffer, data_element, writer_param)
            else:
                writer_function(buffer, data_element)

    # valid pixel data with undefined length shall contain encapsulated
    # data, e.g. sequence items - raise ValueError otherwise (see #238)
    if is_undefined_length and data_element.tag == 0x7fe00010:
        val = data_element.value
        if (fp.is_little_endian and not
                val.startswith(b'\xfe\xff\x00\xe0') or
                not fp.is_little_endian and
                not val.startswith(b'\xff\xfe\xe0\x00')):
            raise ValueError('Pixel Data with undefined length must '
                             'start with an item tag')

    value_length = buffer.tell()
    if (not fp.is_implicit_VR and VR not in extra_length_VRs and
            not is_undefined_length):
        try:
            fp.write_US(value_length)  # Explicit VR length field is 2 bytes
        except struct.error:
            msg = ('The value for the data element {} exceeds the size '
                   'of 64 kByte and cannot be written in an explicit transfer '
                   'syntax. You can save it using Implicit Little Endian '
                   'transfer syntax, or you have to truncate the value to not '
                   'exceed the maximum size of 64 kByte.'
                   .format(data_element.tag))
            raise ValueError(msg)
    else:
        # write the proper length of the data_element in the length slot,
        # unless is SQ with undefined length.
        fp.write_UL(0xFFFFFFFF if is_undefined_length else value_length)

    fp.write(buffer.getvalue())
    if is_undefined_length:
        fp.write_tag(SequenceDelimiterTag)
        fp.write_UL(0)  # 4-byte 'length' of delimiter data item