mcpbeat

Pydicom

k-dense-ai/pydicom

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.

44k tokens
context cost
the whole folder, loaded on every use
13
files
ships runnable scripts
0
copies elsewhere
how many repositories repackaged it
32514
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/K-Dense-AI/scientific-agent-skills --skill pydicom

The instruction itself

14 sections, as written by the author

pydicom

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.

Mandatory safety boundary

  • Work only with local data that the user is authorized to access.
  • DICOM metadata, file names, private elements, overlays, structured content,

and pixels may contain protected health information (PHI).

  • Never print Dataset, export full metadata/JSON, or log element values by

default. Use a documented allowlist and aggregate output.

  • pydicom is a general DICOM framework, not a diagnostic viewer. Pixel output,

validation, conversion, and plugin availability are not diagnostic claims.

  • De-identification is profile-, purpose-, recipient-, jurisdiction-, and

threat-context-specific. It requires privacy/DICOM expert verification.

  • Never claim that a tag-removal script is DICOM PS3.15, HIPAA, GDPR, or other

compliance. Preserve originals and audit derived outputs.

  • Treat deterministic pseudonymization keys and UID maps as re-identification

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.

  • Set explicit input-file, file-count, frame-count, decoded-byte, and output

limits before parsing untrusted or unusually large datasets.

Installation

Create or activate an isolated environment, then install the exact reviewed

release:

uv pip install "pydicom==3.0.2"

Uncompressed pixel arrays and image rendering:

uv pip install "pydicom==3.0.2" "numpy==2.5.1" "Pillow==12.3.0"

Install only the transfer-syntax plugins required by the deployment:

# 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.

Choose the workflow

  • Need an aggregate overview: run scripts/extract_metadata.py.
  • Need bounded technical checks: run scripts/dicom_inventory.py.
  • Need codec deployment preflight: run

scripts/transfer_syntax_inspector.py.

  • Need frame/memory planning: run scripts/pixel_frame_planner.py.
  • Need one non-diagnostic rendered frame: run

scripts/dicom_to_image.py.

  • Need a pseudonymized derivative: read the de-identification section, create

a site-reviewed action profile, then run scripts/anonymize_dicom.py and

scripts/deidentification_audit.py.

  • Need to check a sensitive UID map: run

scripts/uid_mapping_validator.py.

Read datasets safely

dcmread() returns a FileDataset, a Dataset subclass with File Format

state such as file_meta, preamble, and original encoding.

from 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 header

check; it does not prove the bytes are valid DICOM.

Do not call print(ds), repr(ds), or iterate values into logs on clinical

data.

Dataset, DataElement, and sequences

Access standard elements by keyword and check for absence:

modality = 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:

from 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.

UIDs and transfer syntax

The File Meta Information Transfer Syntax UID controls dataset encoding and

pixel compression:

ts = 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.

Pixel data and frames

The stable pydicom.pixels API supports path-based, frame-specific decoding:

from 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:

  • grayscale single frame: (rows, columns)
  • grayscale multi-frame: (frames, rows, columns)
  • color single frame: (rows, columns, samples)
  • color multi-frame: (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:

from 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.

Compression, decompression, and encapsulation

  • Accessing pixel_array decodes as needed but does not change the dataset.
  • Dataset.decompress() changes Pixel Data in place, sets Explicit VR Little

Endian, updates image metadata, and generates a new SOP Instance UID by

default.

  • Dataset.compress(uid) changes Pixel Data and Transfer Syntax in place and

generates a new SOP Instance UID by default.

  • pydicom 3.0 built-in/found encoders cover RLE Lossless, JPEG-LS, and JPEG

2000 combinations documented in the stable plugin matrix.

  • Each compressed frame is separately encoded and then encapsulated. Use

encapsulate() or encapsulate_extended() for externally encoded frames.

  • Read frames with current 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.

DICOM JSON and private elements

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:

# 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.

De-identification workflow

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.

  • Define purpose, recipients, linkage needs, regulations, threat model, and

acceptable re-identification risk.

  • Select the Basic Application Level Confidentiality Profile and needed

options (pixel, recognizable visual features, graphics, structured content,

descriptors, temporal information, patient characteristics, devices,

institutions, UIDs, and safe private data).

  • Preserve source objects unchanged in controlled storage.
  • Apply every action recursively, including nested sequences.
  • Replace instance/reference UIDs consistently across the complete scope;

preserve structural UIDs.

  • Decide date/time handling explicitly. A fixed shift can preserve intervals

but partial dates, time zones, standalone times, leap days, longitudinal

linkage, and external events require reviewed policy.

  • Inspect pixels, overlays, graphics, structured content, and recognizable

visual features. Do not infer clean pixels from missing metadata or set

BurnedInAnnotation=NO without verification.

  • Rebuild File Meta Information and preamble to prevent leakage.
  • Run technical validation and a de-identification audit, then perform expert

verification and documented risk review.

The bundled script intentionally sets PatientIdentityRemoved to NO because

it cannot establish successful de-identification.

Helper CLIs

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.

# 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.

pydicom 3.0 migration notes

  • read_file() and write_file() were removed; use dcmread() and

dcmwrite().

  • write_like_original is deprecated; use enforce_file_format.
  • pydicom.pixel_data_handlers is deprecated for removal in v4; use

pydicom.pixels.

  • Dataset.pixel_array uses the new pixels backend by default and converts

YCbCr 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.

Sources (verified 2026-07-23)

2026-03-19; Python >=3.10.

CVE-2026-32711 details.

PS3.5,

PS3.6,

and PS3.15

How to use it

Copy the folder

Take k-dense-ai/pydicom from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.

Install what it needs

The instructions reference pip, uv. Without those the skill loads but fails at the first command.