RJFuryQuant
RJFuryQuant

Reputation: 21

Is there a Python DICOM Modality Worklist setup library?

I've been down a couple of rabbit holes trying to find suitable ways of creating a DICOM modality worklist (or .wl worklist files rather).

What I have setup so far:

My problem is with the creation of the DICOM text dump files. I'm currently using Python's String.format() function to format a template string. This then substitutes certain placeholders in my template string with actual patient data. Although not elegant, it works. But it's a very static solution and may not be very robust.

Is there a Python library that can be used to generate such text dump files? or even beter, the .wl files? I am willing to trade 3 magic beans, and our family recipe for potato salad, for such a library. (The secret ingredient is not paprika)

For completeness, here is how the template dicom worklist string looks:

dicom_wl_template_string = """
    # Dicom-File-Format
    # Dicom-Meta-Information-Header
    # Used TransferSyntax: Unknown Transfer Syntax

    # Dicom-Data-Set
    # Used TransferSyntax: Little Endian Implicit
    (0002,0000) UL [123]
    (0002,0002) UI [1.2.111.222222.5.1.4.1.1.104.1]
    (0002,0003) UI [1.2.3.4.5.6.132437.17.4.10123450.312346792082.12349.1]
    (0002,0010) UI [1.2.840.10008.1.2.1]
    (0002,0012) UI [1.2.276.0.7230010.3.0.3.6.6]
    (0002,0013) SH [OFFIS_DCMTK_366]

    (0008,0005) CS [{SpecificCharacterSet}]                     #  10, 1 SpecificCharacterSet e.g ISO_IR 100
    (0008,0012) DA [{InstanceCreationDate}]                     #   8, 1 InstanceCreationDate e.g 20220101
    (0008,0050) SH [{AccessionNumber}]                          #   8, 1 AccessionNumber e.g 1234
    (0010,0010) PN [{PatientName}]                              #  14, 1 PatientName e.g SURNAME^ABC
    (0010,0020) LO [{PatientID}]                                #  14, 1 PatientID e.g 7001011234080
    (0010,0030) DA [{PatientBirthDate}]                         #   8, 1 PatientBirthDate e.g. 19700101
    (0010,0040) CS [{PatientSex}]                               #   2, 1 PatientSex e.g. M
    (0020,000d) UI [{StudyInstanceUID}]                         #  54, 1 StudyInstanceUID e.g 1.2.3.4.5.6.132437.17.4.10123450.312346792082.12349.1
    (0032,1060) LO [{RequestedProcedureDescription}]            #  16, 1 RequestedProcedureDescription 
        (0040,0100) SQ (Sequence with explicit length #=1)          #  90, 1 ScheduledProcedureStepSequence
            (fffe,e000) na (Item with ??explicit length #=5??)          #  82, 1 Item
                (0008,0060) CS [{Modality}]                                 #   4, 1 Modality e.g. CT, MR, CR, NM, PT, US, XA 
                (0040,0001) AE [{ScheduledStationAETitle}]                  #   4, 1 ScheduledStationAETitle e.g Foo
                (0040,0002) DA [{ScheduledProcedureStepStartDate}]          #   8, 1 ScheduledProcedureStepStartDate e.g. 20220101
                (0040,0003) TM [{ScheduledProcedureStepStartTime}]          #   8, 1 ScheduledProcedureStepStartTime e.g. 080000
                (0040,0006) PN [{ScheduledPhysicianName}]                   #   8, 1 Scheduled Performing Physicians Name e.g. EMMETBROWN
                (0040,0007) LO [{ScheduledProcedureStepDescription}]        #  22, 1 ScheduledProcedureStepDescription e.g SOMETHING
                (0040,0009) SH [{ScheduledProcedureStepID}]                 #   4, 1 ScheduledProcedureStepID e.g 0001
            (fffe,e00d) na (ItemDelimitationItem for re-encoding)       #   0, 0 ItemDelimitationItem
        (fffe,e0dd) na (SequenceDelimitationItem for re-encod.)     #   0, 0 SequenceDelimitationItem
    (0040,1001) SH [unknown]                                    #   8, 1 RequestedProcedureID
    """

Upvotes: 2

Views: 2215

Answers (2)

RJFuryQuant
RJFuryQuant

Reputation: 21

Thank you for the response. Here is also some example code for future visitors.

import os
from os import path
from pydicom.dataset import Dataset, FileMetaDataset
from pydicom.uid import ExplicitVRLittleEndian
        
wl_file_name = "directory/file.wl"
txt_file_name = "directory/file.txt"

# Create data set
ds = Dataset()
# Add file meta information elements
ds.file_meta = FileMetaDataset()
ds.file_meta.TransferSyntaxUID = ExplicitVRLittleEndian
ds.file_meta.MediaStorageSOPClassUID = "0"
ds.file_meta.MediaStorageSOPInstanceUID = "0"
# Fill out the worklist query elements
ds.SpecificCharacterSet             = "ISO_IR 6"
ds.InstanceCreationDate             = "20220101"
ds.AccessionNumber                  = "12345-abc"
ds.PatientName                      = "SURNAME^NAME"
ds.PatientID                        = "123456"
ds.PatientBirthDate                 = "19700101"
ds.PatientSex                       = "M"
ds.StudyInstanceUID                 = "1a-2b-3c" 
ds.RequestedProcedureDescription    = "ProcedureDescription"
ds.ScheduledProcedureStepSequence   = [Dataset()]
ds.ScheduledProcedureStepSequence[0].Modality                           = "OT"
ds.ScheduledProcedureStepSequence[0].ScheduledStationAETitle            = "OT"
ds.ScheduledProcedureStepSequence[0].ScheduledProcedureStepStartDate    = "20220101"
ds.ScheduledProcedureStepSequence[0].ScheduledProcedureStepStartTime    = "080000"
ds.ScheduledProcedureStepSequence[0].ScheduledPerformingPhysicianName   = "Doctor Emmet Brown"
ds.ScheduledProcedureStepSequence[0].ScheduledProcedureStepDescription  = "SchedProcStepDesc"
ds.ScheduledProcedureStepID         = "0001"
# more stuff if you need

# Save directly as a .wl file.
# Set write_like_original=False to be certain you’re writing the dataset in the DICOM File Format 
ds.save_as(wl_file_name, write_like_original=False)

# Additionally, you can also make a readable txt file for humans
# Check if txt file already exists
if(path.exists(txt_file_name)): #if txt file exists, remove it first
    try:
        os.remove(txt_file_name)
    except OSError as e:
        print("Error: %s : %s" % (txt_file_name, e.strerror))
# Run dcmdump command to convert wl file to txt
convert_wl_to_txt_cmd = "dcmdump " + wl_file_name + " > "  + txt_file_name 
os.system(convert_wl_to_txt_cmd)

The txt version then looks like this:

# Dicom-File-Format

# Dicom-Meta-Information-Header
# Used TransferSyntax: Little Endian Explicit
(0002,0000) UL 120                                      #   4, 1 FileMetaInformationGroupLength
(0002,0001) OB 00\01                                    #   2, 1 FileMetaInformationVersion
(0002,0002) UI [0]                                      #   2, 1 MediaStorageSOPClassUID
(0002,0003) UI [0]                                      #   2, 1 MediaStorageSOPInstanceUID
(0002,0010) UI =LittleEndianExplicit                    #  20, 1 TransferSyntaxUID
(0002,0012) UI [1.2.826.0.1.3680043.8.498.1]            #  28, 1 ImplementationClassUID
(0002,0013) SH [PYDICOM 2.2.2]                          #  14, 1 ImplementationVersionName

# Dicom-Data-Set
# Used TransferSyntax: Little Endian Explicit
(0008,0005) CS [ISO_IR 6]                               #   8, 1 SpecificCharacterSet
(0008,0012) DA [20220101]                               #   8, 1 InstanceCreationDate
(0008,0050) SH [12345-abc]                              #  10, 1 AccessionNumber
(0010,0010) PN [SURNAME^NAME]                           #  12, 1 PatientName
(0010,0020) LO [123456]                                 #   6, 1 PatientID
(0010,0030) DA [19700101]                               #   8, 1 PatientBirthDate
(0010,0040) CS [M]                                      #   2, 1 PatientSex
(0020,000d) UI [1a-2b-3c]                               #   8, 1 StudyInstanceUID
(0032,1060) LO [ProcedureDescription]                   #  20, 1 RequestedProcedureDescription
(0040,0009) SH [0001]                                   #   4, 1 ScheduledProcedureStepID
(0040,0100) SQ (Sequence with explicit length #=1)      # 110, 1 ScheduledProcedureStepSequence
  (fffe,e000) na (Item with explicit length #=6)          # 102, 1 Item
    (0008,0060) CS [OT]                                     #   2, 1 Modality
    (0040,0001) AE [OT]                                     #   2, 1 ScheduledStationAETitle
    (0040,0002) DA [20220101]                               #   8, 1 ScheduledProcedureStepStartDate
    (0040,0003) TM [080000]                                 #   6, 1 ScheduledProcedureStepStartTime
    (0040,0006) PN [Doctor Emmet Brown]                     #  18, 1 ScheduledPerformingPhysicianName
    (0040,0007) LO [SchedProcStepDesc]                      #  18, 1 ScheduledProcedureStepDescription
  (fffe,e00d) na (ItemDelimitationItem for re-encoding)   #   0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) #   0, 0 SequenceDelimitationItem

Upvotes: 0

scaramallion
scaramallion

Reputation: 1300

pydicom should be able to do this and probably allow you to skip the text dump step (disclaimer - I'm a contributor to pydicom):

from pydicom.dataset import Dataset, FileMetaDataset
from pydicom.uid import ExplicitVRLittleEndian

ds = Dataset()

# Add file meta information elements
ds.file_meta = FileMetaDataset()
ds.file_meta.TransferSyntaxUID = ExplicitVRLittleEndian

# Fill out the worklist query elements
ds.SpecificCharacterSet = "ISO_IR 6"
ds.ScheduledProcedureStepSequence = [Dataset()]
ds.ScheduledProcedureStepSequence[0].Modality = "CT"
# etc...

ds.save_as("query.wl", write_like_original=False)

If you decide to go this way, the dataset introduction tutorial is a good place to start.

Upvotes: 0

Related Questions