def get_nr_fragments(fp: DicomFileLike) -> int: """Return the number of fragments in `fp`. .. versionadded:: 1.4 """ if not fp.is_little_endian: raise ValueError("'fp.is_little_endian' must be True") nr_fragments = 0 start = fp.tell() while True: try: tag = Tag(fp.read_tag()) except EOFError: break if tag == 0xFFFEE000: # Item length = fp.read_UL() if length == 0xFFFFFFFF: raise ValueError("Undefined item length at offset {} when " "parsing the encapsulated pixel data " "fragments.".format(fp.tell() - 4)) fp.seek(length, 1) nr_fragments += 1 elif tag == 0xFFFEE0DD: # Sequence Delimiter break else: raise ValueError("Unexpected tag '{}' at offset {} when parsing " "the encapsulated pixel data fragment items." .format(tag, fp.tell() - 4)) fp.seek(start) return nr_fragments
def read_item(fp: DicomFileLike) -> Optional[bytes]: """Read and return a single Item in the fragmented data stream. Parameters ---------- fp : filebase.DicomIO The file-like to read the item from. Returns ------- bytes The Item's raw bytes. """ logger = pydicom.config.logger try: tag = fp.read_tag() # already read delimiter before passing data here # so should just run out except EOFError: return None # No more items, time for sequence to stop reading if tag == SequenceDelimiterTag: 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
def generate_pixel_data_fragment( fp: DicomFileLike ) -> Generator[bytes, None, None]: """Yield the encapsulated pixel data fragments. For compressed (encapsulated) Transfer Syntaxes, the (7FE0,0010) *Pixel Data* element is encoded in an encapsulated format. **Encapsulation** The encoded pixel data stream is fragmented into one or more Items. The stream may represent a single or multi-frame image. Each *Data Stream Fragment* shall have tag of (FFFE,E000), followed by a 4 byte *Item Length* field encoding the explicit number of bytes in the Item. All Items containing an encoded fragment shall have an even number of bytes greater than or equal to 2, with the last fragment being padded if necessary. The first Item in the Sequence of Items shall be a 'Basic Offset Table', however the Basic Offset Table item value is not required to be present. It is assumed that the Basic Offset Table item has already been read prior to calling this function (and that `fp` is positioned past this item). The remaining items in the Sequence of Items are the pixel data fragments and it is these items that will be read and returned by this function. The Sequence of Items is terminated by a (FFFE,E0DD) *Sequence Delimiter Item* with an Item Length field of value ``0x00000000``. The presence or absence of the *Sequence Delimiter Item* in `fp` has no effect on the returned fragments. *Encoding* The encoding of the data shall be little endian. Parameters ---------- fp : filebase.DicomFileLike The encoded (7FE0,0010) *Pixel Data* element value, positioned at the start of the item tag for the first item after the Basic Offset Table item. ``fp.is_little_endian`` should be set to ``True``. Yields ------ bytes A pixel data fragment. Raises ------ ValueError If the data contains an item with an undefined length or an unknown tag. References ---------- DICOM Standard Part 5, :dcm:`Annex A.4 <part05/sect_A.4.html>` """ if not fp.is_little_endian: raise ValueError("'fp.is_little_endian' must be True") # We should be positioned at the start of the Item Tag for the first # fragment after the Basic Offset Table while True: try: tag = Tag(fp.read_tag()) except EOFError: break if tag == 0xFFFEE000: # Item length = fp.read_UL() if length == 0xFFFFFFFF: raise ValueError("Undefined item length at offset {} when " "parsing the encapsulated pixel data " "fragments.".format(fp.tell() - 4)) yield fp.read(length) elif tag == 0xFFFEE0DD: # Sequence Delimiter # Behave nicely and rewind back to the end of the items fp.seek(-4, 1) break else: raise ValueError("Unexpected tag '{0}' at offset {1} when parsing " "the encapsulated pixel data fragment items." .format(tag, fp.tell() - 4))