Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use pydicom to read, inspect, write, transform, and safely preflight local DICOM datasets and pixel data. Applies to DICOM metadata, transfer syntaxes, compression plugins, frames, private elements, JSON, and bounded de-identification review.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 74% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 231% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 122% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 42% | 0% |
Use pydicom for DICOM dataset I/O and pixel processing. Version 3.0.2 is the current stable release reviewed here. It fixes CVE-2026-32711, a crafted DICOMDIR path-traversal issue. pydicom 3.0.2 declares Python >=3.10; its bundled DICOM dictionary is 2024c, while the live DICOM Standard may be newer.
and pixels may contain protected health information (PHI).
Dataset, export full metadata/JSON, or log element values bydefault. Use a documented allowlist and aggregate output.
validation, conversion, and plugin availability are not diagnostic claims.
threat-context-specific. It requires privacy/DICOM expert verification.
compliance. Preserve originals and audit derived outputs.
secrets: use least privilege and encrypted/managed secret storage, never commit, sync, log, or share them with derivatives, and define backup, rotation, revocation, and destruction procedures. A leaked key invalidates the intended separation; rotation also changes deterministic mappings.
limits before parsing untrusted or unusually large datasets.
Create or activate an isolated environment, then install the exact reviewed release:
bashuv pip install "pydicom==3.0.2"
Uncompressed pixel arrays and image rendering:
bashuv pip install "pydicom==3.0.2" "numpy==2.5.1" "Pillow==12.3.0"
Install only the transfer-syntax plugins required by the deployment:
bash# JPEG/JPEG-LS, JPEG 2000/HTJ2K, and faster RLE through pylibjpeg uv pip install "numpy==2.5.1" "pylibjpeg==2.1.0" \ "pylibjpeg-libjpeg==2.4.0" "pylibjpeg-openjpeg==2.5.0" \ "pylibjpeg-rle==2.2.0" # JPEG-LS encoder/decoder uv pip install "numpy==2.5.1" "pyjpegls==1.5.1" # Alternative decoder with platform-specific wheels uv pip install "python-gdcm==3.2.6"
Plugin licenses and wheels differ by package/platform; review them before deployment. Pillow has documented decoding limitations and pydicom cautions that plugin output must be independently checked.
Native codec wheels widen the supply-chain and memory-safety boundary. For a controlled deployment, resolve these exact pins on a trusted build host, lock and verify wheel hashes/provenance, mirror approved artifacts internally, scan them, and install with hash enforcement rather than resolving from the public index at runtime.
scripts/extract_metadata.py.scripts/dicom_inventory.py.scripts/transfer_syntax_inspector.py.
scripts/pixel_frame_planner.py.scripts/dicom_to_image.py.
a site-reviewed action profile, then run scripts/anonymize_dicom.py and scripts/deidentification_audit.py.
scripts/uid_mapping_validator.py.
dcmread() returns a FileDataset, a Dataset subclass with File Format state such as file_meta, preamble, and original encoding.
pythonfrom pathlib import Path import pydicom path = Path("authorized/input.dcm") ds = pydicom.dcmread( path, stop_before_pixels=True, specific_tags=[ "SOPClassUID", "Modality", "Rows", "Columns", "NumberOfFrames", ], ) technical = { "sop_class": ds.get("SOPClassUID"), "modality": ds.get("Modality"), "rows": ds.get("Rows"), "columns": ds.get("Columns"), }
Use:
stop_before_pixels=True for metadata-only work.specific_tags=[...] for a minimum allowlist.defer_size="1 MiB" when a later write must preserve large values.force=False (default). force=True only bypasses the File Format headercheck; it does not prove the bytes are valid DICOM.
Do not call print(ds), repr(ds), or iterate values into logs on clinical data.
Access standard elements by keyword and check for absence:
pythonmodality = ds.get("Modality", "UNSPECIFIED") if "ReferencedImageSequence" in ds: for item in ds.ReferencedImageSequence: referenced_class = item.get("ReferencedSOPClassUID")
Tag access, such as ds[0x0010, 0x0010], returns a DataElement; its .value is separate. Sequence behaves like a list of nested Dataset items. Privacy actions must recurse through every sequence item, not only the top level.
When creating a file, use FileMetaDataset for group 0002, keep dataset and file-meta SOP UIDs consistent, set a Transfer Syntax UID, and write in enforced File Format:
pythonfrom pydicom import dcmwrite from pydicom.dataset import FileDataset, FileMetaDataset from pydicom.uid import CTImageStorage, ExplicitVRLittleEndian, generate_uid meta = FileMetaDataset() meta.MediaStorageSOPClassUID = CTImageStorage meta.MediaStorageSOPInstanceUID = generate_uid() meta.TransferSyntaxUID = ExplicitVRLittleEndian ds = FileDataset(None, {}, file_meta=meta, preamble=b"\0" * 128) ds.SOPClassUID = meta.MediaStorageSOPClassUID ds.SOPInstanceUID = meta.MediaStorageSOPInstanceUID # Add all attributes required by the selected IOD before writing. dcmwrite("new.dcm", ds, enforce_file_format=True, overwrite=False)
write_like_original is deprecated in pydicom 3.0; use enforce_file_format. A successful write is not full PS3.3 IOD conformance.
The File Meta Information Transfer Syntax UID controls dataset encoding and pixel compression:
pythonts = ds.file_meta.TransferSyntaxUID summary = { "uid": str(ts), "name": ts.name, "compressed": ts.is_compressed, "implicit_vr": ts.is_implicit_VR, "little_endian": ts.is_little_endian, }
pydicom 3.0 chooses write encoding from the Transfer Syntax UID before legacy dataset flags. Do not replace structural UIDs (Transfer Syntax, SOP Class, or coding-scheme UIDs) during pseudonymization. Instance/reference UID replacement must be one-to-one and consistent across the complete declared scope.
Read references/transfer_syntaxes.md before compression, decompression, or encapsulation.
The stable pydicom.pixels API supports path-based, frame-specific decoding:
pythonfrom pydicom.pixels import pixel_array # Reads only the selected frame where the source permits it. frame = pixel_array("authorized/image.dcm", index=0, raw=False)
Shape semantics:
(rows, columns)(frames, rows, columns)(rows, columns, samples)(frames, rows, columns, samples)raw=False converts YCbCr pixel data to RGB when possible; raw=True retains the decoded color space after mandatory minimal processing. Use iter_pixels(path, indices=[...]) for bounded multi-frame iteration.
For grayscale display, apply transforms in this order:
pythonfrom pydicom.pixels import apply_modality_lut, apply_voi_lut modality_values = apply_modality_lut(frame, ds) display_values = apply_voi_lut(modality_values, ds, index=0)
Modality LUT/rescale and VOI/windowing change display/value semantics. MONOCHROME1 may require presentation inversion. Palette Color requires apply_color_lut(). Presentation states and ICC behavior may require a validated viewer. Never use per-frame min/max normalization for quantitative analysis.
pixel_array decodes as needed but does not change the dataset.Dataset.decompress() changes Pixel Data in place, sets Explicit VR LittleEndian, updates image metadata, and generates a new SOP Instance UID by default.
Dataset.compress(uid) changes Pixel Data and Transfer Syntax in place andgenerates a new SOP Instance UID by default.
2000 combinations documented in the stable plugin matrix.
encapsulate() or encapsulate_extended() for externally encoded frames.
pydicom.encaps.generate_frames() or get_frame();legacy encapsulation generator names are deprecated for pydicom 4.
Always inspect capabilities first, limit decoded bytes/frames, and verify pixel correctness independently. Lossy compression acceptability is outside pydicom and the DICOM encoding specification.
Dataset.to_json(), to_json_dict(), and Dataset.from_json() implement the DICOM JSON Model, but pydicom documents JSON support as beta. Full JSON may inline binary data and expose every identifier and pixel payload. Do not emit it as a metadata report. A BulkDataURI handler introduces separate storage, authorization, and retrieval obligations.
Private elements are not standardized and may contain PHI:
python# Recursive removal, but not sufficient de-identification by itself. ds.remove_private_tags()
Retain private elements only under an explicit reviewed safe-private policy. Read references/common_tags.md for tag access, privacy classes, and standard pointers.
DICOM PS3.15 Annex E explicitly states that confidentiality profiles do not guarantee removal of all identifying information and do not replace a complete de-identification process.
acceptable re-identification risk.
options (pixel, recognizable visual features, graphics, structured content, descriptors, temporal information, patient characteristics, devices, institutions, UIDs, and safe private data).
preserve structural UIDs.
but partial dates, time zones, standalone times, leap days, longitudinal linkage, and external events require reviewed policy.
visual features. Do not infer clean pixels from missing metadata or set BurnedInAnnotation=NO without verification.
verification and documented risk review.
The bundled script intentionally sets PatientIdentityRemoved to NO because it cannot establish successful de-identification.
All --help paths are dependency-free. The tools perform no network access and emit no DICOM values beyond narrow technical allowlists.
Bundled content consists of the two linked references, the documented helper scripts, and synthetic tests. The pydicom runtime dependency is installed from the pinned PyPI release.
bash# Redacted aggregate metadata python scripts/extract_metadata.py authorized/ --recursive # Metadata-only technical inventory python scripts/dicom_inventory.py authorized/ --recursive # Installed codec/plugin capabilities python scripts/transfer_syntax_inspector.py --input authorized/image.dcm # Frame shape, byte, and transform plan python scripts/pixel_frame_planner.py authorized/image.dcm --frames 0,2-4 # One non-diagnostic frame python scripts/dicom_to_image.py authorized/image.dcm frame.png \ --acknowledge-pixel-phi # Create a secret key, then a scoped pseudonymized derivative plus audit python scripts/anonymize_dicom.py --generate-uid-key project.key python scripts/anonymize_dicom.py authorized/in.dcm derived/out.dcm \ --uid-key-file project.key --uid-scope export-v1 \ --audit-report derived/out.audit.json # Audit candidate metadata; no pixel decompression python scripts/deidentification_audit.py derived/out.dcm # Validate an explicitly requested sensitive UID mapping python scripts/uid_mapping_validator.py derived/uid-map.json \ --uid-key-file project.key --uid-scope export-v1
The generated raw key file is a controlled-local convenience and is created with owner-only permissions. For production, materialize key bytes from an approved secret manager into a locked ephemeral file, restrict access to the de-identification service, and securely remove it afterward. Store any optional UID map separately from derivatives; it directly links original and replacement identifiers.
read_file() and write_file() were removed; use dcmread() anddcmwrite().
write_like_original is deprecated; use enforce_file_format.pydicom.pixel_data_handlers is deprecated for removal in v4; usepydicom.pixels.
Dataset.pixel_array uses the new pixels backend by default and convertsYCbCr to RGB when possible.
JPEGLossless now means UID 1.2.840.10008.1.2.4.57;JPEGLosslessSV1 is .70.
Dataset.is_little_endian and is_implicit_VR are deprecated for v4.2026-03-19; Python >=3.10.
CVE-2026-32711 details.
Other measured skills in the registry, with their headline benchmark lift.