pydicom

Par mkurman · zorai

Bibliothèque Python pour travailler avec les fichiers DICOM (Digital Imaging and Communications in Medicine). Utilisez cette skill pour lire, écrire ou modifier des données d'imagerie médicale au format DICOM, extraire des données pixel d'images médicales (CT, IRM, radiographie, échographie), anonymiser des fichiers DICOM, manipuler les métadonnées et tags DICOM, convertir des images DICOM vers d'autres formats, gérer des données DICOM compressées, ou traiter des jeux de données d'imagerie médicale. S'applique aux tâches d'analyse d'images médicales, aux systèmes PACS, aux flux de travail en radiologie et aux applications d'imagerie en santé.

npx skills add https://github.com/mkurman/zorai --skill pydicom

Pydicom

Overview

Pydicom est un package Python pur pour travailler avec des fichiers DICOM, le format standard pour les données d'imagerie médicale. Cette skill fournit des orientations sur la lecture, l'écriture et la manipulation de fichiers DICOM, y compris le travail avec les données de pixels, les métadonnées et divers formats de compression.

Quand utiliser cette skill

Utilisez cette skill lors du travail avec :

  • Des fichiers d'imagerie médicale (TDM, IRM, rayons X, échographie, TEP, etc.)
  • Des datasets DICOM nécessitant l'extraction ou la modification de métadonnées
  • L'extraction de données de pixels et le traitement d'images à partir de scans médicaux
  • L'anonymisation DICOM pour la recherche ou le partage de données
  • La conversion de fichiers DICOM en formats d'image standard
  • Les données DICOM compressées nécessitant une décompression
  • Les séquences DICOM et les rapports structurés
  • La reconstruction de volumes multi-coupes
  • L'intégration de PACS (Picture Archiving and Communication System)

Installation

Installez pydicom et les dépendances courantes :

uv pip install pydicom
uv pip install pillow  # For image format conversion
uv pip install numpy   # For pixel array manipulation
uv pip install matplotlib  # For visualization

Pour gérer les fichiers DICOM compressés, des packages supplémentaires peuvent être nécessaires :

uv pip install pylibjpeg pylibjpeg-libjpeg pylibjpeg-openjpeg  # JPEG compression
uv pip install python-gdcm  # Alternative compression handler

Workflows principaux

Lire des fichiers DICOM

Lisez un fichier DICOM en utilisant pydicom.dcmread() :

import pydicom

# Read a DICOM file
ds = pydicom.dcmread('path/to/file.dcm')

# Access metadata
print(f"Patient Name: {ds.PatientName}")
print(f"Study Date: {ds.StudyDate}")
print(f"Modality: {ds.Modality}")

# Display all elements
print(ds)

Points clés :

  • dcmread() retourne un objet Dataset
  • Accédez aux éléments de données en utilisant la notation d'attribut (p. ex. ds.PatientName) ou la notation de tag (p. ex. ds[0x0010, 0x0010])
  • Utilisez ds.file_meta pour accéder aux métadonnées du fichier comme Transfer Syntax UID
  • Gérez les attributs manquants avec getattr(ds, 'AttributeName', default_value) ou hasattr(ds, 'AttributeName')

Travailler avec les données de pixels

Extrayez et manipulez les données d'image à partir de fichiers DICOM :

import pydicom
import numpy as np
import matplotlib.pyplot as plt

# Read DICOM file
ds = pydicom.dcmread('image.dcm')

# Get pixel array (requires numpy)
pixel_array = ds.pixel_array

# Image information
print(f"Shape: {pixel_array.shape}")
print(f"Data type: {pixel_array.dtype}")
print(f"Rows: {ds.Rows}, Columns: {ds.Columns}")

# Apply windowing for display (CT/MRI)
if hasattr(ds, 'WindowCenter') and hasattr(ds, 'WindowWidth'):
    from pydicom.pixel_data_handlers.util import apply_voi_lut
    windowed_image = apply_voi_lut(pixel_array, ds)
else:
    windowed_image = pixel_array

# Display image
plt.imshow(windowed_image, cmap='gray')
plt.title(f"{ds.Modality} - {ds.StudyDescription}")
plt.axis('off')
plt.show()

Travailler avec les images en couleur :

# RGB images have shape (rows, columns, 3)
if ds.PhotometricInterpretation == 'RGB':
    rgb_image = ds.pixel_array
    plt.imshow(rgb_image)
elif ds.PhotometricInterpretation == 'YBR_FULL':
    from pydicom.pixel_data_handlers.util import convert_color_space
    rgb_image = convert_color_space(ds.pixel_array, 'YBR_FULL', 'RGB')
    plt.imshow(rgb_image)

Images multi-frames (vidéos/séries) :

# For multi-frame DICOM files
if hasattr(ds, 'NumberOfFrames') and ds.NumberOfFrames > 1:
    frames = ds.pixel_array  # Shape: (num_frames, rows, columns)
    print(f"Number of frames: {frames.shape[0]}")

    # Display specific frame
    plt.imshow(frames[0], cmap='gray')

Convertir DICOM en formats d'image

Utilisez le script dicom_to_image.py fourni ou convertissez manuellement :

from PIL import Image
import pydicom
import numpy as np

ds = pydicom.dcmread('input.dcm')
pixel_array = ds.pixel_array

# Normalize to 0-255 range
if pixel_array.dtype != np.uint8:
    pixel_array = ((pixel_array - pixel_array.min()) /
                   (pixel_array.max() - pixel_array.min()) * 255).astype(np.uint8)

# Save as PNG
image = Image.fromarray(pixel_array)
image.save('output.png')

Utilisez le script : python scripts/dicom_to_image.py input.dcm output.png

Modifier les métadonnées

Modifiez les éléments de données DICOM :

import pydicom
from datetime import datetime

ds = pydicom.dcmread('input.dcm')

# Modify existing elements
ds.PatientName = "Doe^John"
ds.StudyDate = datetime.now().strftime('%Y%m%d')
ds.StudyDescription = "Modified Study"

# Add new elements
ds.SeriesNumber = 1
ds.SeriesDescription = "New Series"

# Remove elements
if hasattr(ds, 'PatientComments'):
    delattr(ds, 'PatientComments')
# Or using del
if 'PatientComments' in ds:
    del ds.PatientComments

# Save modified file
ds.save_as('modified.dcm')

Anonymiser les fichiers DICOM

Supprimez ou remplacez les informations d'identification personnelles du patient :

import pydicom
from datetime import datetime

ds = pydicom.dcmread('input.dcm')

# Tags commonly containing PHI (Protected Health Information)
tags_to_anonymize = [
    'PatientName', 'PatientID', 'PatientBirthDate',
    'PatientSex', 'PatientAge', 'PatientAddress',
    'InstitutionName', 'InstitutionAddress',
    'ReferringPhysicianName', 'PerformingPhysicianName',
    'OperatorsName', 'StudyDescription', 'SeriesDescription',
]

# Remove or replace sensitive data
for tag in tags_to_anonymize:
    if hasattr(ds, tag):
        if tag in ['PatientName', 'PatientID']:
            setattr(ds, tag, 'ANONYMOUS')
        elif tag == 'PatientBirthDate':
            setattr(ds, tag, '19000101')
        else:
            delattr(ds, tag)

# Update dates to maintain temporal relationships
if hasattr(ds, 'StudyDate'):
    # Shift dates by a random offset
    ds.StudyDate = '20000101'

# Keep pixel data intact
ds.save_as('anonymized.dcm')

Utilisez le script fourni : python scripts/anonymize_dicom.py input.dcm output.dcm

Écrire des fichiers DICOM

Créez des fichiers DICOM à partir de zéro :

import pydicom
from pydicom.dataset import Dataset, FileDataset
from datetime import datetime
import numpy as np

# Create file meta information
file_meta = Dataset()
file_meta.MediaStorageSOPClassUID = pydicom.uid.generate_uid()
file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()
file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian

# Create the FileDataset instance
ds = FileDataset('new_dicom.dcm', {}, file_meta=file_meta, preamble=b"\0" * 128)

# Add required DICOM elements
ds.PatientName = "Test^Patient"
ds.PatientID = "123456"
ds.Modality = "CT"
ds.StudyDate = datetime.now().strftime('%Y%m%d')
ds.StudyTime = datetime.now().strftime('%H%M%S')
ds.ContentDate = ds.StudyDate
ds.ContentTime = ds.StudyTime

# Add image-specific elements
ds.SamplesPerPixel = 1
ds.PhotometricInterpretation = "MONOCHROME2"
ds.Rows = 512
ds.Columns = 512
ds.BitsAllocated = 16
ds.BitsStored = 16
ds.HighBit = 15
ds.PixelRepresentation = 0

# Create pixel data
pixel_array = np.random.randint(0, 4096, (512, 512), dtype=np.uint16)
ds.PixelData = pixel_array.tobytes()

# Add required UIDs
ds.SOPClassUID = pydicom.uid.CTImageStorage
ds.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID
ds.SeriesInstanceUID = pydicom.uid.generate_uid()
ds.StudyInstanceUID = pydicom.uid.generate_uid()

# Save the file
ds.save_as('new_dicom.dcm')

Compression et décompression

Gérez les fichiers DICOM compressés :

import pydicom

# Read compressed DICOM file
ds = pydicom.dcmread('compressed.dcm')

# Check transfer syntax
print(f"Transfer Syntax: {ds.file_meta.TransferSyntaxUID}")
print(f"Transfer Syntax Name: {ds.file_meta.TransferSyntaxUID.name}")

# Decompress and save as uncompressed
ds.decompress()
ds.save_as('uncompressed.dcm', write_like_original=False)

# Or compress when saving (requires appropriate encoder)
ds_uncompressed = pydicom.dcmread('uncompressed.dcm')
ds_uncompressed.compress(pydicom.uid.JPEGBaseline8Bit)
ds_uncompressed.save_as('compressed_jpeg.dcm')

Syntaxes de transfert courantes :

  • ExplicitVRLittleEndian - Non compressé, le plus courant
  • JPEGBaseline8Bit - Compression JPEG avec perte
  • JPEGLossless - Compression JPEG sans perte
  • JPEG2000Lossless - JPEG 2000 sans perte
  • RLELossless - Codage RLE (Run-Length Encoding) sans perte

Voir references/transfer_syntaxes.md pour la liste complète.

Travailler avec les séquences DICOM

Gérez les structures de données imbriquées :

import pydicom

ds = pydicom.dcmread('file.dcm')

# Access sequences
if 'ReferencedStudySequence' in ds:
    for item in ds.ReferencedStudySequence:
        print(f"Referenced SOP Instance UID: {item.ReferencedSOPInstanceUID}")

# Create a sequence
from pydicom.sequence import Sequence

sequence_item = Dataset()
sequence_item.ReferencedSOPClassUID = pydicom.uid.CTImageStorage
sequence_item.ReferencedSOPInstanceUID = pydicom.uid.generate_uid()

ds.ReferencedImageSequence = Sequence([sequence_item])

Traiter les séries DICOM

Travaillez avec plusieurs fichiers DICOM connexes :

import pydicom
import numpy as np
from pathlib import Path

# Read all DICOM files in a directory
dicom_dir = Path('dicom_series/')
slices = []

for file_path in dicom_dir.glob('*.dcm'):
    ds = pydicom.dcmread(file_path)
    slices.append(ds)

# Sort by slice location or instance number
slices.sort(key=lambda x: float(x.ImagePositionPatient[2]))
# Or: slices.sort(key=lambda x: int(x.InstanceNumber))

# Create 3D volume
volume = np.stack([s.pixel_array for s in slices])
print(f"Volume shape: {volume.shape}")  # (num_slices, rows, columns)

# Get spacing information for proper scaling
pixel_spacing = slices[0].PixelSpacing  # [row_spacing, col_spacing]
slice_thickness = slices[0].SliceThickness
print(f"Voxel size: {pixel_spacing[0]}x{pixel_spacing[1]}x{slice_thickness} mm")

Scripts utilitaires

Cette skill inclut des scripts utilitaires dans le répertoire scripts/ :

anonymize_dicom.py

Anonymisez les fichiers DICOM en supprimant ou remplaçant les informations de santé protégées (PHI).

python scripts/anonymize_dicom.py input.dcm output.dcm

dicom_to_image.py

Convertissez les fichiers DICOM en formats d'image courants (PNG, JPEG, TIFF).

python scripts/dicom_to_image.py input.dcm output.png
python scripts/dicom_to_image.py input.dcm output.jpg --format JPEG

extract_metadata.py

Extrayez et affichez les métadonnées DICOM dans un format lisible.

python scripts/extract_metadata.py file.dcm
python scripts/extract_metadata.py file.dcm --output metadata.txt

Matériel de référence

Des informations de référence détaillées sont disponibles dans le répertoire references/ :

  • common_tags.md : Liste complète des tags DICOM couramment utilisés organisée par catégorie (Patient, Study, Series, Image, etc.)
  • transfer_syntaxes.md : Référence complète des syntaxes de transfert DICOM et des formats de compression

Problèmes courants et solutions

Problème : « Unable to decode pixel data »

  • Solution : Installez les gestionnaires de compression supplémentaires : uv pip install pylibjpeg pylibjpeg-libjpeg python-gdcm

Problème : « AttributeError » lors de l'accès aux tags

  • Solution : Vérifiez l'existence de l'attribut avec hasattr(ds, 'AttributeName') ou utilisez ds.get('AttributeName', default)

Problème : Affichage incorrect de l'image (trop sombre/clair)

  • Solution : Appliquez le windowing VOI LUT : apply_voi_lut(pixel_array, ds) ou ajustez manuellement avec WindowCenter et WindowWidth

Problème : Problèmes de mémoire avec des séries volumineuses

  • Solution : Traitez les fichiers itérativement, utilisez des tableaux mappés en mémoire, ou réduisez l'échelle des images

Bonnes pratiques

  1. Vérifiez toujours les attributs requis avant d'y accéder en utilisant hasattr() ou get()
  2. Préservez les métadonnées du fichier lors de la modification des fichiers en utilisant save_as() avec write_like_original=True
  3. Utilisez les Transfer Syntax UIDs pour comprendre le format de compression avant de traiter les données de pixels
  4. Gérez les exceptions lors de la lecture de fichiers provenant de sources non fiables
  5. Appliquez le windowing approprié (VOI LUT) pour la visualisation d'images médicales
  6. Maintenez les informations spatiales (espacement des pixels, épaisseur de coupe) lors du traitement des volumes 3D
  7. Vérifiez l'anonymisation minutieusement avant de partager des données médicales
  8. Utilisez les UIDs correctement - générez de nouveaux UIDs lors de la création de nouvelles instances, préservez-les lors de la modification

Documentation

Documentation officielle de pydicom : https://pydicom.github.io/pydicom/dev/

Skills similaires