Skip to main content

dicom_header

Single-read DICOM header access and metadata-only frame counting.

Reading a DICOM over a network share is dominated by round trips, not by bytes transferred: every open() is an SMB2 CREATE. This module reads a file once into memory and exposes the parsed dataset plus the bytes at the start of the Pixel Data element, so callers can re-parse as often as they like without touching the share again.

It also derives the frame count from element metadata rather than by decoding pixel data. NumberOfFrames is absent from most single-frame images and from non-image DICOMs, and some converted files omit it while genuinely holding multiple frames, so a frame count has to come from somewhere; taking it from the Pixel Data length costs twelve bytes instead of a full transfer and decode.

Module​

Functions​

frames_from_header​

def frames_from_header(header: DicomHeaderRead) ‑> int | None:

Derive the frame count from element metadata, without decoding pixels.

Tries, in order: the Number of Frames tag; the Pixel Data length divided by the size of one frame (exact for uncompressed data); the Extended Offset Table; the Basic Offset Table.

Arguments

  • header: A header read by read_dicom_header.

Returns The frame count, or None if it cannot be determined from metadata alone — in which case the caller must fall back to reading pixel data.

media_storage_sop_class_name​

def media_storage_sop_class_name(ds: pydicom.Dataset) ‑> str:

The Media Storage SOP Class UID's name, from an already-parsed dataset.

read_dicom_header​

def read_dicom_header(    filename: str | os.PathLike[str], *, head_bytes: int = 131072,) ‑> DicomHeaderRead:

Read a DICOM file's header in a single open.

Reads head_bytes and parses from memory. If the parse runs out of bytes before reaching the Pixel Data element, the rest of the file is read from the same handle — an extra transfer, but not an extra open.

The file's size is never queried: a read returning fewer bytes than asked for has reached the end, which answers the only question size would. Over SMB a stat of an open handle is itself a round trip, which is the cost this function exists to avoid.

Arguments

  • filename: The file to read.
  • head_bytes: How much to read on the first attempt.

Returns The parsed header and the bytes it came from.

Raises

  • FileNotFoundError: If the file is missing. Callers are expected to handle this, since the file may reappear on a later run.

Classes​

DicomHeaderRead​

class DicomHeaderRead(    ds: pydicom.FileDataset,    raw: bytes,    pixel_data_offset: int | None,    reached_pixel_data: bool,    complete: bool,    whole_file: bool,):

The result of reading a DICOM file's header in a single open.

Attributes

  • ds: The parsed dataset, stopping at the Pixel Data element.
  • raw: The bytes read from the file.
  • pixel_data_offset: Offset into raw of the Pixel Data element tag, or None if the parse did not reach one.
  • reached_pixel_data: Whether the parse stopped because it found a Pixel Data element. False with complete True means the file genuinely has no pixel data (e.g. a structured report).
  • complete: Whether raw holds the whole file, or enough of it to have reached the Pixel Data element. When False the dataset may carry silently truncated element values.
  • whole_file: Whether raw is the entire file rather than a prefix of it. Stronger than complete, which a conventional file satisfies from its first megabyte: a caller that needs the pixel data itself - the Zeiss route, whose image lives in private tags - can parse raw instead of opening the file again only when this is True.

Variables​

  • static complete : bool
  • static ds : pydicom.dataset.FileDataset
  • static pixel_data_offset : int | None
  • static raw : bytes
  • static reached_pixel_data : bool
  • static whole_file : bool