def read_dicomdir(filename: PathType = "DICOMDIR") -> DicomDir: """Read a DICOMDIR file and return a :class:`~pydicom.dicomdir.DicomDir`. This is a wrapper around :func:`dcmread` which gives a default file name. .. deprecated:: 2.1 ``read_dicomdir()`` is deprecated and will be removed in v3.0. Use :func:`~pydicom.filereader.dcmread` instead. Parameters ---------- filename : str, optional Full path and name to DICOMDIR file to open Returns ------- DicomDir Raises ------ InvalidDicomError Raised if filename is not a DICOMDIR file. """ warnings.warn( "'read_dicomdir()' is deprecated and will be removed in v3.0, use " "'dcmread()' instead", DeprecationWarning) str_or_obj = path_from_pathlike(filename) ds = dcmread(str_or_obj) if not isinstance(ds, DicomDir): raise InvalidDicomError( f"File '{filename!r}' is not a Media Storage Directory file") return ds
def get_files(base, pattern): """Return all files from a set of sources. Parameters ---------- base : str or PathLike Base directory to recursively search. pattern : str A string pattern to filter the files. Default is "*" and it will return all files. Returns ------- files : list of str The list of filenames matched. """ base = path_from_pathlike(base) # if the user forgot to add them pattern = "*" + pattern + "*" files = [] for root, _, filenames in os.walk(base): for filename in filenames: filename_filter = fnmatch.filter([join(root, filename)], pattern) if len(filename_filter): files.append(filename_filter[0]) return files
def get_files(base, pattern): """Return all files from a set of sources. Parameters ---------- base : str or PathLike Base directory to recursively search. pattern : str A string pattern to filter the files. Default is "*" and it will return all files. Returns ------- files : list of str The list of filenames matched. """ base = path_from_pathlike(base) # if the user forgot to add them pattern = "*" + pattern + "*" files = [] for root, _, filenames in os.walk(base): for filename in filenames: filename_filter = fnmatch.filter([join(root, filename)], pattern) if len(filename_filter): files.append(filename_filter[0]) # To preserve backwards compatibility filter the downloaded files # as if they are stored within DATA_ROOT/test_files/*.dcm dummy_online_file_path_map = online_test_file_dummy_paths() dummy_online_file_path_filtered = fnmatch.filter( dummy_online_file_path_map.keys(), join(base, pattern)) download_names = [ str(dummy_online_file_path_map[dummy_path]) for dummy_path in dummy_online_file_path_filtered ] real_online_file_paths = [] for filename in download_names: real_online_file_paths.append( str(download.data_path_with_download(filename))) files += real_online_file_paths return files
def dcmread(fp, defer_size=None, stop_before_pixels=False, force=False, specific_tags=None): """Read and parse a DICOM dataset stored in the DICOM File Format. Read a DICOM dataset stored in accordance with the :dcm:`DICOM File Format <part10/chapter_7.html>`. If the dataset is not stored in accordance with the File Format (i.e. the preamble and prefix are missing, there are missing required Type 1 *File Meta Information Group* elements or the entire *File Meta Information* is missing) then you will have to set `force` to ``True``. Parameters ---------- fp : str or PathLike or file-like Either a file-like object, or a string containing the file name. If a file-like object, the caller is responsible for closing it. defer_size : int or str or None, optional If ``None`` (default), all elements are read into memory. If specified, then if a data element's stored value is larger than `defer_size`, the value is not read into memory until it is accessed in code. Specify an integer (bytes), or a string value with units, e.g. "512 KB", "2 MB". stop_before_pixels : bool, optional If ``False`` (default), the full file will be read and parsed. Set ``True`` to stop before reading (7FE0,0010) *Pixel Data* (and all subsequent elements). force : bool, optional If ``False`` (default), raises an :class:`~pydicom.errors.InvalidDicomError` if the file is missing the *File Meta Information* header. Set to ``True`` to force reading even if no *File Meta Information* header is found. specific_tags : list or None, optional If not ``None``, only the tags in the list are returned. The list elements can be tags or tag names. Note that the element (0008,0005) *Specific Character Set* is always returned if present - this ensures correct decoding of returned text values. Returns ------- FileDataset An instance of :class:`~pydicom.dataset.FileDataset` that represents a parsed DICOM file. Raises ------ InvalidDicomError If `force` is ``True`` and the file is not a valid DICOM file. See Also -------- pydicom.dataset.FileDataset Data class that is returned. pydicom.filereader.read_partial Only read part of a DICOM file, stopping on given conditions. Examples -------- Read and return a dataset stored in accordance with the DICOM File Format: >>> ds = pydicom.dcmread("rtplan.dcm") >>> ds.PatientName Read and return a dataset not in accordance with the DICOM File Format: >>> ds = pydicom.dcmread("rtplan.dcm", force=True) >>> ds.PatientName Use within a context manager: >>> with pydicom.dcmread("rtplan.dcm") as ds: >>> ds.PatientName """ # Open file if not already a file object caller_owns_file = True fp = path_from_pathlike(fp) if isinstance(fp, str): # caller provided a file name; we own the file handle caller_owns_file = False try: logger.debug(u"Reading file '{0}'".format(fp)) except Exception: logger.debug("Reading file '{0}'".format(fp)) fp = open(fp, 'rb') if config.debugging: logger.debug("\n" + "-" * 80) logger.debug("Call to dcmread()") msg = ("filename:'%s', defer_size='%s', " "stop_before_pixels=%s, force=%s, specific_tags=%s") logger.debug( msg % (fp.name, defer_size, stop_before_pixels, force, specific_tags)) if caller_owns_file: logger.debug("Caller passed file object") else: logger.debug("Caller passed file name") logger.debug("-" * 80) # Convert size to defer reading into bytes defer_size = size_in_bytes(defer_size) # Iterate through all items and store them --include file meta if present stop_when = None if stop_before_pixels: stop_when = _at_pixel_data try: dataset = read_partial(fp, stop_when, defer_size=defer_size, force=force, specific_tags=specific_tags) finally: if not caller_owns_file: fp.close() # XXX need to store transfer syntax etc. return dataset
def dcmwrite(filename: Union[PathType, BinaryIO], dataset: Dataset, write_like_original: bool = True) -> None: """Write `dataset` to the `filename` specified. If `write_like_original` is ``True`` then the :class:`Dataset` will be written as is (after minimal validation checking) and may or may not contain all or parts of the *File Meta Information* (and hence may or may not be conformant with the DICOM File Format). If `write_like_original` is ``False``, `dataset` will be stored in the :dcm:`DICOM File Format <part10/chapter_7.html>`. To do so requires that the ``Dataset.file_meta`` attribute exists and contains a :class:`Dataset` with the required (Type 1) *File Meta Information Group* elements. The byte stream of the `dataset` will be placed into the file after the DICOM *File Meta Information*. **File Meta Information** The *File Meta Information* consists of a 128-byte preamble, followed by a 4 byte ``b'DICM'`` prefix, followed by the *File Meta Information Group* elements. **Preamble and Prefix** The ``dataset.preamble`` attribute shall be 128-bytes long or ``None`` and is available for use as defined by the Application Profile or specific implementations. If the preamble is not used by an Application Profile or specific implementation then all 128 bytes should be set to ``0x00``. The actual preamble written depends on `write_like_original` and ``dataset.preamble`` (see the table below). +------------------+------------------------------+ | | write_like_original | +------------------+-------------+----------------+ | dataset.preamble | True | False | +==================+=============+================+ | None | no preamble | 128 0x00 bytes | +------------------+-------------+----------------+ | 128 bytes | dataset.preamble | +------------------+------------------------------+ The prefix shall be the bytestring ``b'DICM'`` and will be written if and only if the preamble is present. **File Meta Information Group Elements** The preamble and prefix are followed by a set of DICOM elements from the (0002,eeee) group. Some of these elements are required (Type 1) while others are optional (Type 3/1C). If `write_like_original` is ``True`` then the *File Meta Information Group* elements are all optional. See :func:`~pydicom.filewriter.write_file_meta_info` for more information on which elements are required. The *File Meta Information Group* elements should be included within their own :class:`~pydicom.dataset.Dataset` in the ``dataset.file_meta`` attribute. If (0002,0010) *Transfer Syntax UID* is included then the user must ensure its value is compatible with the values for the ``dataset.is_little_endian`` and ``dataset.is_implicit_VR`` attributes. For example, if ``is_little_endian`` and ``is_implicit_VR`` are both ``True`` then the Transfer Syntax UID must be 1.2.840.10008.1.2 *Implicit VR Little Endian*. See the DICOM Standard, Part 5, :dcm:`Section 10<part05/chapter_10.html>` for more information on Transfer Syntaxes. *Encoding* The preamble and prefix are encoding independent. The File Meta elements are encoded as *Explicit VR Little Endian* as required by the DICOM Standard. **Dataset** A DICOM Dataset representing a SOP Instance related to a DICOM Information Object Definition. It is up to the user to ensure the `dataset` conforms to the DICOM Standard. *Encoding* The `dataset` is encoded as specified by the ``dataset.is_little_endian`` and ``dataset.is_implicit_VR`` attributes. It's up to the user to ensure these attributes are set correctly (as well as setting an appropriate value for ``dataset.file_meta.TransferSyntaxUID`` if present). Parameters ---------- filename : str or PathLike or file-like Name of file or the file-like to write the new DICOM file to. dataset : pydicom.dataset.FileDataset Dataset holding the DICOM information; e.g. an object read with :func:`~pydicom.filereader.dcmread`. write_like_original : bool, optional If ``True`` (default), preserves the following information from the Dataset (and may result in a non-conformant file): - preamble -- if the original file has no preamble then none will be written. - file_meta -- if the original file was missing any required *File Meta Information Group* elements then they will not be added or written. If (0002,0000) *File Meta Information Group Length* is present then it may have its value updated. - seq.is_undefined_length -- if original had delimiters, write them now too, instead of the more sensible length characters - is_undefined_length_sequence_item -- for datasets that belong to a sequence, write the undefined length delimiters if that is what the original had. If ``False``, produces a file conformant with the DICOM File Format, with explicit lengths for all elements. Raises ------ AttributeError If either ``dataset.is_implicit_VR`` or ``dataset.is_little_endian`` have not been set. ValueError If group 2 elements are in ``dataset`` rather than ``dataset.file_meta``, or if a preamble is given but is not 128 bytes long, or if Transfer Syntax is a compressed type and pixel data is not compressed. See Also -------- pydicom.dataset.Dataset Dataset class with relevant attributes and information. pydicom.dataset.Dataset.save_as Write a DICOM file from a dataset that was read in with ``dcmread()``. ``save_as()`` wraps ``dcmwrite()``. """ # Ensure is_little_endian and is_implicit_VR are set if None in (dataset.is_little_endian, dataset.is_implicit_VR): has_tsyntax = False try: tsyntax = dataset.file_meta.TransferSyntaxUID if not tsyntax.is_private: dataset.is_little_endian = tsyntax.is_little_endian dataset.is_implicit_VR = tsyntax.is_implicit_VR has_tsyntax = True except AttributeError: pass if not has_tsyntax: name = dataset.__class__.__name__ raise AttributeError( f"'{name}.is_little_endian' and '{name}.is_implicit_VR' must " f"be set appropriately before saving") # Try and ensure that `is_undefined_length` is set correctly try: tsyntax = dataset.file_meta.TransferSyntaxUID if not tsyntax.is_private: dataset['PixelData'].is_undefined_length = tsyntax.is_compressed except (AttributeError, KeyError): pass # Check that dataset's group 0x0002 elements are only present in the # `dataset.file_meta` Dataset - user may have added them to the wrong # place if dataset.group_dataset(0x0002) != Dataset(): raise ValueError( f"File Meta Information Group Elements (0002,eeee) should be in " f"their own Dataset object in the " f"'{dataset.__class__.__name__}.file_meta' attribute.") # A preamble is required under the DICOM standard, however if # `write_like_original` is True we treat it as optional preamble = getattr(dataset, 'preamble', None) if preamble and len(preamble) != 128: raise ValueError( f"'{dataset.__class__.__name__}.preamble' must be 128-bytes long.") if not preamble and not write_like_original: # The default preamble is 128 0x00 bytes. preamble = b'\x00' * 128 # File Meta Information is required under the DICOM standard, however if # `write_like_original` is True we treat it as optional if not write_like_original: # the checks will be done in write_file_meta_info() dataset.fix_meta_info(enforce_standard=False) else: dataset.ensure_file_meta() # Check for decompression, give warnings if inconsistencies # If decompressed, then pixel_array is now used instead of PixelData if dataset.is_decompressed: if dataset.file_meta.TransferSyntaxUID.is_compressed: raise ValueError( f"The Transfer Syntax UID element in " f"'{dataset.__class__.__name__}.file_meta' is compressed " f"but the pixel data has been decompressed") # Force PixelData to the decompressed version dataset.PixelData = dataset.pixel_array.tobytes() caller_owns_file = True # Open file if not already a file object filename = path_from_pathlike(filename) if isinstance(filename, str): fp = DicomFile(filename, 'wb') # caller provided a file name; we own the file handle caller_owns_file = False else: try: fp = DicomFileLike(filename) except AttributeError: raise TypeError("dcmwrite: Expected a file path or a file-like, " "but got " + type(filename).__name__) try: # WRITE FILE META INFORMATION if preamble: # Write the 'DICM' prefix if and only if we write the preamble fp.write(preamble) fp.write(b'DICM') tsyntax: Optional[UID] = None # type: ignore[no-redef] if dataset.file_meta: # May be an empty Dataset # If we want to `write_like_original`, don't enforce_standard write_file_meta_info(fp, dataset.file_meta, enforce_standard=not write_like_original) tsyntax = getattr(dataset.file_meta, "TransferSyntaxUID", None) if (tsyntax == DeflatedExplicitVRLittleEndian): # See PS3.5 section A.5 # when writing, the entire dataset following # the file metadata is prepared the normal way, # then "deflate" compression applied. buffer = DicomBytesIO() _write_dataset(buffer, dataset, write_like_original) # Compress the encoded data and write to file compressor = zlib.compressobj(wbits=-zlib.MAX_WBITS) deflated = compressor.compress( buffer.parent.getvalue() # type: ignore[union-attr] ) deflated += compressor.flush() if len(deflated) % 2: deflated += b'\x00' fp.write(deflated) else: _write_dataset(fp, dataset, write_like_original) finally: if not caller_owns_file: fp.close()
def dcmwrite(filename, dataset, write_like_original=True): """Write `dataset` to the `filename` specified. If `write_like_original` is ``True`` then `dataset` will be written as is (after minimal validation checking) and may or may not contain all or parts of the File Meta Information (and hence may or may not be conformant with the DICOM File Format). If `write_like_original` is ``False``, `dataset` will be stored in the :dcm:`DICOM File Format <part10/chapter_7.html>`. To do so requires that the ``Dataset.file_meta`` attribute exists and contains a :class:`Dataset` with the required (Type 1) *File Meta Information Group* elements. The byte stream of the `dataset` will be placed into the file after the DICOM *File Meta Information*. If `write_like_original` is ``True`` then the :class:`Dataset` will be written as is (after minimal validation checking) and may or may not contain all or parts of the *File Meta Information* (and hence may or may not be conformant with the DICOM File Format). **File Meta Information** The *File Meta Information* consists of a 128-byte preamble, followed by a 4 byte ``b'DICM'`` prefix, followed by the *File Meta Information Group* elements. **Preamble and Prefix** The ``dataset.preamble`` attribute shall be 128-bytes long or ``None`` and is available for use as defined by the Application Profile or specific implementations. If the preamble is not used by an Application Profile or specific implementation then all 128 bytes should be set to ``0x00``. The actual preamble written depends on `write_like_original` and ``dataset.preamble`` (see the table below). +------------------+------------------------------+ | | write_like_original | +------------------+-------------+----------------+ | dataset.preamble | True | False | +==================+=============+================+ | None | no preamble | 128 0x00 bytes | +------------------+-------------+----------------+ | 128 bytes | dataset.preamble | +------------------+------------------------------+ The prefix shall be the bytestring ``b'DICM'`` and will be written if and only if the preamble is present. **File Meta Information Group Elements** The preamble and prefix are followed by a set of DICOM elements from the (0002,eeee) group. Some of these elements are required (Type 1) while others are optional (Type 3/1C). If `write_like_original` is ``True`` then the *File Meta Information Group* elements are all optional. See :func:`~pydicom.filewriter.write_file_meta_info` for more information on which elements are required. The *File Meta Information Group* elements should be included within their own :class:`~pydicom.dataset.Dataset` in the ``dataset.file_meta`` attribute. If (0002,0010) *Transfer Syntax UID* is included then the user must ensure its value is compatible with the values for the ``dataset.is_little_endian`` and ``dataset.is_implicit_VR`` attributes. For example, if ``is_little_endian`` and ``is_implicit_VR`` are both ``True`` then the Transfer Syntax UID must be 1.2.840.10008.1.2 *Implicit VR Little Endian*. See the DICOM Standard, Part 5, :dcm:`Section 10<part05/chapter_10.html>` for more information on Transfer Syntaxes. *Encoding* The preamble and prefix are encoding independent. The File Meta elements are encoded as *Explicit VR Little Endian* as required by the DICOM Standard. **Dataset** A DICOM Dataset representing a SOP Instance related to a DICOM Information Object Definition. It is up to the user to ensure the `dataset` conforms to the DICOM Standard. *Encoding* The `dataset` is encoded as specified by the ``dataset.is_little_endian`` and ``dataset.is_implicit_VR`` attributes. It's up to the user to ensure these attributes are set correctly (as well as setting an appropriate value for ``dataset.file_meta.TransferSyntaxUID`` if present). Parameters ---------- filename : str or PathLike or file-like Name of file or the file-like to write the new DICOM file to. dataset : pydicom.dataset.FileDataset Dataset holding the DICOM information; e.g. an object read with :func:`~pydicom.filereader.dcmread`. write_like_original : bool, optional If ``True`` (default), preserves the following information from the Dataset (and may result in a non-conformant file): - preamble -- if the original file has no preamble then none will be written. - file_meta -- if the original file was missing any required *File Meta Information Group* elements then they will not be added or written. If (0002,0000) *File Meta Information Group Length* is present then it may have its value updated. - seq.is_undefined_length -- if original had delimiters, write them now too, instead of the more sensible length characters - is_undefined_length_sequence_item -- for datasets that belong to a sequence, write the undefined length delimiters if that is what the original had. If ``False``, produces a file conformant with the DICOM File Format, with explicit lengths for all elements. Raises ------ AttributeError If either ``dataset.is_implicit_VR`` or ``dataset.is_little_endian`` have not been set. ValueError If group 2 elements are in ``dataset`` rather than ``dataset.file_meta``, or if a preamble is given but is not 128 bytes long, or if Transfer Syntax is a compressed type and pixel data is not compressed. See Also -------- pydicom.dataset.FileDataset Dataset class with relevant attributes and information. pydicom.dataset.Dataset.save_as Write a DICOM file from a dataset that was read in with ``dcmread()``. ``save_as()`` wraps ``dcmwrite()``. """ # Ensure is_little_endian and is_implicit_VR are set if None in (dataset.is_little_endian, dataset.is_implicit_VR): has_tsyntax = False try: tsyntax = dataset.file_meta.TransferSyntaxUID if not tsyntax.is_private: dataset.is_little_endian = tsyntax.is_little_endian dataset.is_implicit_VR = tsyntax.is_implicit_VR has_tsyntax = True except AttributeError: pass if not has_tsyntax: raise AttributeError( "'{0}.is_little_endian' and '{0}.is_implicit_VR' must be " "set appropriately before saving.".format( dataset.__class__.__name__)) # Try and ensure that `is_undefined_length` is set correctly try: tsyntax = dataset.file_meta.TransferSyntaxUID if not tsyntax.is_private: dataset['PixelData'].is_undefined_length = tsyntax.is_compressed except (AttributeError, KeyError): pass # Check that dataset's group 0x0002 elements are only present in the # `dataset.file_meta` Dataset - user may have added them to the wrong # place if dataset.group_dataset(0x0002) != Dataset(): raise ValueError("File Meta Information Group Elements (0002,eeee) " "should be in their own Dataset object in the " "'{0}.file_meta' " "attribute.".format(dataset.__class__.__name__)) # A preamble is required under the DICOM standard, however if # `write_like_original` is True we treat it as optional preamble = getattr(dataset, 'preamble', None) if preamble and len(preamble) != 128: raise ValueError("'{0}.preamble' must be 128-bytes " "long.".format(dataset.__class__.__name__)) if not preamble and not write_like_original: # The default preamble is 128 0x00 bytes. preamble = b'\x00' * 128 # File Meta Information is required under the DICOM standard, however if # `write_like_original` is True we treat it as optional if not write_like_original: # the checks will be done in write_file_meta_info() dataset.fix_meta_info(enforce_standard=False) else: dataset.ensure_file_meta() # Check for decompression, give warnings if inconsistencies # If decompressed, then pixel_array is now used instead of PixelData if dataset.is_decompressed: xfer = dataset.file_meta.TransferSyntaxUID if xfer not in UncompressedPixelTransferSyntaxes: raise ValueError("file_meta transfer SyntaxUID is compressed type " "but pixel data has been decompressed") # Force PixelData to the decompressed version dataset.PixelData = dataset.pixel_array.tobytes() caller_owns_file = True # Open file if not already a file object filename = path_from_pathlike(filename) if isinstance(filename, str): fp = DicomFile(filename, 'wb') # caller provided a file name; we own the file handle caller_owns_file = False else: fp = DicomFileLike(filename) # if we want to write with the same endianess and VR handling as # the read dataset we want to preserve raw data elements for # performance reasons (which is done by get_item); # otherwise we use the default converting item getter if dataset.is_original_encoding: get_item = Dataset.get_item else: get_item = Dataset.__getitem__ try: # WRITE FILE META INFORMATION if preamble: # Write the 'DICM' prefix if and only if we write the preamble fp.write(preamble) fp.write(b'DICM') if dataset.file_meta: # May be an empty Dataset # If we want to `write_like_original`, don't enforce_standard write_file_meta_info(fp, dataset.file_meta, enforce_standard=not write_like_original) # WRITE DATASET # The transfer syntax used to encode the dataset can't be changed # within the dataset. # Write any Command Set elements now as elements must be in tag order # Mixing Command Set with other elements is non-conformant so we # require `write_like_original` to be True command_set = get_item(dataset, slice(0x00000000, 0x00010000)) if command_set and write_like_original: fp.is_implicit_VR = True fp.is_little_endian = True write_dataset(fp, command_set) # Set file VR and endianness. MUST BE AFTER writing META INFO (which # requires Explicit VR Little Endian) and COMMAND SET (which requires # Implicit VR Little Endian) fp.is_implicit_VR = dataset.is_implicit_VR fp.is_little_endian = dataset.is_little_endian # Write non-Command Set elements now write_dataset(fp, get_item(dataset, slice(0x00010000, None))) finally: if not caller_owns_file: fp.close()
def dcmread( fp: Union[PathType, BinaryIO], defer_size: Optional[Union[str, int, float]] = None, stop_before_pixels: bool = False, force: bool = False, specific_tags: Optional[TagListType] = None ) -> Union[FileDataset, DicomDir]: """Read and parse a DICOM dataset stored in the DICOM File Format. Read a DICOM dataset stored in accordance with the :dcm:`DICOM File Format <part10/chapter_7.html>`. If the dataset is not stored in accordance with the File Format (i.e. the preamble and prefix are missing, there are missing required Type 1 *File Meta Information Group* elements or the entire *File Meta Information* is missing) then you will have to set `force` to ``True``. .. deprecated:: 2.2 Returning a :class:`~pydicom.dicomdir.DicomDir` is deprecated and will be removed in v3.0. Use :class:`~pydicom.fileset.FileSet` instead. Examples -------- Read and return a dataset stored in accordance with the DICOM File Format: >>> ds = pydicom.dcmread("CT_small.dcm") >>> ds.PatientName Read and return a dataset not in accordance with the DICOM File Format: >>> ds = pydicom.dcmread("rtplan.dcm", force=True) >>> ds.PatientName Use within a context manager: >>> with pydicom.dcmread("rtplan.dcm") as ds: ... ds.PatientName Parameters ---------- fp : str or PathLike or file-like Either a file-like object, a string containing the file name or the path to the file. The file-like object must have ``seek()``, ``read()`` and ``tell()`` methods and the caller is responsible for closing it (if required). defer_size : int, str or float, optional If not used then all elements are read into memory. If specified, then if a data element's stored value is larger than `defer_size`, the value is not read into memory until it is accessed in code. Should be the number of bytes to be read as :class:`int` or as a :class:`str` with units, e.g. ``'512 KB'``, ``'2 MB'``. stop_before_pixels : bool, optional If ``False`` (default), the full file will be read and parsed. Set ``True`` to stop before reading (7FE0,0010) *Pixel Data* (and all subsequent elements). force : bool, optional If ``False`` (default), raises an :class:`~pydicom.errors.InvalidDicomError` if the file is missing the *File Meta Information* header. Set to ``True`` to force reading even if no *File Meta Information* header is found. specific_tags : list of (int or str or 2-tuple of int), optional If used the only the supplied tags will be returned. The supplied elements can be tags or keywords. Note that the element (0008,0005) *Specific Character Set* is always returned if present - this ensures correct decoding of returned text values. Returns ------- FileDataset or DicomDir An instance of :class:`~pydicom.dataset.FileDataset` that represents a parsed DICOM file, unless the dataset is a *Media Storage Directory* instance in which case it will be a :class:`~pydicom.dicomdir.DicomDir`. Raises ------ InvalidDicomError If `force` is ``False`` and the file is not a valid DICOM file. TypeError If `fp` is ``None`` or of an unsupported type. See Also -------- pydicom.dataset.FileDataset Data class that is returned. pydicom.filereader.read_partial Only read part of a DICOM file, stopping on given conditions. """ # Open file if not already a file object caller_owns_file = True fp = path_from_pathlike(fp) if isinstance(fp, str): # caller provided a file name; we own the file handle caller_owns_file = False logger.debug("Reading file '{0}'".format(fp)) fp = open(fp, 'rb') elif fp is None or not hasattr(fp, "read") or not hasattr(fp, "seek"): raise TypeError("dcmread: Expected a file path or a file-like, " "but got " + type(fp).__name__) if config.debugging: logger.debug("\n" + "-" * 80) logger.debug("Call to dcmread()") msg = ("filename:'%s', defer_size='%s', " "stop_before_pixels=%s, force=%s, specific_tags=%s") logger.debug(msg % (fp.name, defer_size, stop_before_pixels, force, specific_tags)) if caller_owns_file: logger.debug("Caller passed file object") else: logger.debug("Caller passed file name") logger.debug("-" * 80) if specific_tags: specific_tags = [Tag(t) for t in specific_tags] specific_tags = cast(Optional[List[BaseTag]], specific_tags) # Iterate through all items and store them --include file meta if present stop_when = None if stop_before_pixels: stop_when = _at_pixel_data try: dataset = read_partial( fp, stop_when, defer_size=size_in_bytes(defer_size), force=force, specific_tags=specific_tags, ) finally: if not caller_owns_file: fp.close() # XXX need to store transfer syntax etc. return dataset
def test_path_like(self): assert 'test.dcm' == path_from_pathlike(PathLike('test.dcm'))
def test_pathlib_path(self): assert 'test.dcm' == path_from_pathlike(Path('test.dcm'))
def test_non_pathlike_is_returned_unaltered(self): assert 'test.dcm' == path_from_pathlike('test.dcm') assert path_from_pathlike(None) is None file_like = BytesIO() assert file_like == path_from_pathlike(file_like) assert 42 == path_from_pathlike(42)