Create RDSR from Toshiba CT dose summary images and import

Issue #480 resolved
David Platten created an issue

Create RDSR from Toshiba CT dose summary images, update the information using tags in associated images, then import in to OpenREM.

Comments (82)

  1. David Platten reporter

    Initial commit for code to create RDSR from Toshiba dose summary image, and then update it with additional information contained in the associated images. Currently the code will create an RDSR and then add acquisition protocol names to each acquisition if they are missing from the intial RDSR but available in the CT image tags. References issue #480

    → <<cset 174f83b33f47>>

  2. David Platten reporter

    Initial commit for code to create RDSR from Toshiba dose summary image, and then update it with additional information contained in the associated images. Currently the code will create an RDSR and then add acquisition protocol names to each acquisition if they are missing from the intial RDSR but available in the CT image tags. References issue #480

    → <<cset fc57a19ccca6>>

  3. David Platten reporter

    @edmcdonagh. Apologies: at first I created this branch from the wrong starting branch (a 0.5 version), so I've had to remove it, and then add it again.

  4. Ed McDonagh

    That would explain the odd message I had from QuantifiedCode referring to commits I couldn't find!

  5. David Platten reporter

    The pitch should have this construction (from a Siemens RDSR):

       ---------
       (0040, a010) Relationship Type                   CS: 'CONTAINS'
       (0040, a040) Value Type                          CS: 'NUM'
       (0040, a043)  Concept Name Code Sequence   1 item(s) ----
          (0008, 0100) Code Value                          SH: '113828'
          (0008, 0102) Coding Scheme Designator            SH: 'DCM'
          (0008, 0104) Code Meaning                        LO: 'Pitch Factor'
          ---------
       (0040, a300)  Measured Value Sequence   1 item(s) ----
          (0040, 08ea)  Measurement Units Code Sequence   1 item(s) ----
             (0008, 0100) Code Value                          SH: '{ratio}'
             (0008, 0102) Coding Scheme Designator            SH: 'UCUM'
             (0008, 0103) Coding Scheme Version               SH: '1.4'
             (0008, 0104) Code Meaning                        LO: 'ratio'
             ---------
          (0040, a30a) Numeric Value                       DS: '0.6'
          ---------
       ---------
    

    My code currently produces this:

      ---------
      (0040, a010) Relationship Type                   CS: 'CONTAINS'
      (0040, a040) Value Type                          CS: 'NUM'
      (0040, a043)  Concept Name Code Sequence   1 item(s) ---- 
         (0008, 0100) Code Value                          SH: '113828'
         (0008, 0102) Coding Scheme Designator            SH: 'DCM'
         (0008, 0104) Code Meaning                        LO: 'Pitch Factor'
         ---------
      (0040, a300)  Measured Value Sequence   1 item(s) ---- 
         (0040, 08ea)  Measurement Units Code Sequence   1 item(s) ---- 
            (0008, 0100) Code Value                          SH: '{ratio}'
            (0008, 0102) Coding Scheme Designator            SH: 'UCUM'
            (0008, 0103) Coding Scheme Version               SH: '1.4'
            (0008, 0104) Code Meaning                        LO: 'ratio'
            ---------
         ---------
      (0040, a30a) Numeric Value                       DS: '0.844'
      ---------
    

    The Numeric Value isn't in the right place. @edmcdonagh, can you help?

  6. Ed McDonagh

    What about:

    pitch_value = Dataset()
    pitch_value.NumericValue = 0.6
    measured_value_sequence = Sequence([measurement_units_container, pitch_value])
    
  7. David Platten reporter

    That modifies things a bit, but it's still not right, as there's now 2 items in the Measured Value Sequence...

      ---------
      (0040, a010) Relationship Type                   CS: 'CONTAINS'
      (0040, a040) Value Type                          CS: 'NUM'
      (0040, a043)  Concept Name Code Sequence   1 item(s) ---- 
         (0008, 0100) Code Value                          SH: '113828'
         (0008, 0102) Coding Scheme Designator            SH: 'DCM'
         (0008, 0104) Code Meaning                        LO: 'Pitch Factor'
         ---------
      (0040, a300)  Measured Value Sequence   2 item(s) ---- 
         (0040, 08ea)  Measurement Units Code Sequence   1 item(s) ---- 
            (0008, 0100) Code Value                          SH: '{ratio}'
            (0008, 0102) Coding Scheme Designator            SH: 'UCUM'
            (0008, 0103) Coding Scheme Version               SH: '1.4'
            (0008, 0104) Code Meaning                        LO: 'ratio'
            ---------
         ---------
         (0040, a30a) Numeric Value                       DS: '0.844'
         ---------
      ---------
    
  8. Ed McDonagh

    Your first attempt was nearly right. Just the NumericValue was one level too deep:

    coding.CodeValue = '113828'
    coding.CodingSchemeDesignator = "DCM"
    coding.CodeMeaning = "Pitch Factor"
    # Create the second inner coding bit
    coding2.CodeValue = '{ratio}'
    coding2.CodingSchemeDesignator = "UCUM"
    coding2.CodingSchemeVersion = "1.4"
    coding2.CodeMeaning = "ratio"
    measurement_units_container = Dataset()
    measurement_units_container.MeasurementUnitsCodeSequence = Sequence([coding2])
    # next line is the one that is changed
    measurement_units_container.NumericValue = val
    measured_value_sequence = Sequence([measurement_units_container])
    # Create the outer container bit
    pitch_container = Dataset()
    pitch_container.RelationshipType = "CONTAINS"
    pitch_container.ValueType = "NUM"
    # Add the coding sequence into the container.
    # Sequences are lists.
    pitch_container.ConceptNameCodeSequence = Sequence([coding])
    pitch_container.MeasuredValueSequence = measured_value_sequence
    container2b.ContentSequence.append(pitch_container)
    
  9. Ed McDonagh

    If I've made the same corrections in the code above as I did in my freehand shell version, you should get the same as I did:

    >>> pitch_container
    (0040, a010) Relationship Type                   CS: 'CONTAINS'
    (0040, a040) Value Type                          CS: 'NUM'
    (0040, a043)  Concept Name Code Sequence   1 item(s) ---- 
       (0008, 0100) Code Value                          SH: '113828'
       (0008, 0102) Coding Scheme Designator            SH: 'DCM'
       (0008, 0104) Code Meaning                        LO: 'Pitch Factor'
       ---------
    (0040, a300)  Measured Value Sequence   1 item(s) ---- 
       (0040, 08ea)  Measurement Units Code Sequence   1 item(s) ---- 
          (0008, 0100) Code Value                          SH: '{ratio}'
          (0008, 0102) Coding Scheme Designator            SH: 'UCUM'
          (0008, 0103) Coding Scheme Version               SH: '1.4'
          (0008, 0104) Code Meaning                        LO: 'ratio'
          ---------
       (0040, a30a) Numeric Value                       DS: '0.844'
       ---------
    
  10. David Platten reporter

    I'll add a "CT X-Ray Source Parameters" container to each "CT Acquisition Parameters" container next so that I can add kVp, exposure time per rotation etc. to the created RDSR.

  11. David Platten reporter

    Modified the file layout so that it can be called using python.

    Added import in to OpenREM.

    Adding the extra study information doesn't seem to work at the moment.

    References issue #480

    → <<cset 6271254faa42>>

  12. David Platten reporter

    Updated how study-level information is found in the images and then used to update the initial RDSR. Need to see how this works with a clinical scan. References issue #480.

    → <<cset cf10b607d783>>

  13. David Platten reporter

    Need to store the folders for java.exe, dcmtk and pixelmed.jar somewhere in the system so that I don't have to keep changing them from home to work... Perhaps in local_settings.py?

  14. Ed McDonagh

    Either in local_settings.py or in a singleton in the database. Probably easier to put it in local_settings.py for now and we can reconsider later.

  15. David Platten reporter

    Put paths to various tools in local_settings.py.example. Import these into the Toshiba RDSR creation routine. Should make it easier when moving from one system to the next, and I think a good idea to keep the configuration in one place. References issue #480.

    → <<cset faef3f382d06>>

  16. David Platten reporter

    Requested procedure now obtained from two possible locations in the image data. Uniqueness of acquisitions improved by combining acquisition number with acquisition time. Included the Lua script that I am using to run this routine. Conquest is configured to run the script using an import converter like this:

    ImportModality4 = CT
    ImportConverter4 = process patient after 0 by openrem_import_ct.lua %p::%V0008,0070::%V0008,1090::%V0018,1020::%V0008,1010;
    

    → <<cset 9de77fc4ba9e>>

  17. David Platten reporter

    I need to obtain the contents of "SoftwareVersions" (0018,1020) from the CT images and add it to the appropriate place in the RDSR.

  18. David Platten reporter

    Updated routine to create RDSR from Toshiba dose summary image. This is now called from a script in the Scripts Python folder. The routine itself is now run using celery by including @shared_task before the routine. This fixes a problem that I was having where importing Toshiba CT data using this routine blocked the celery task queue. References issue #480. I've also added an example dicom.ini file for Conquest, together with some Lua scripts that I am using to import data. I am now using Lua scripts in preference to Windows batch files. The current scripts contain some Windows-specific things, so aren't completely general at the moment. This references issue #150

    → <<cset 2bbfc1f23d02>>

  19. David Platten reporter

    Added the Toshiba CT rdsr creation routine to the extractor init.py file. Fixed an error in the extractor code. Made python line in script the same as other extractors. References issue #480

    → <<cset 5256f5f64b81>>

  20. David Platten reporter

    Small updates to Toshiba CT RDSR creation. Updated example dicom.ini files and associated Lua scripts. Also added example Windows PowerShell scripts that I am using to schedule query-retrieve from PACS. Also added example batch file that runs celery. Updated celery settings in settings.py to reflect what I am using successfully here. References issue #480

    → <<cset 7cc8190761ce>>

  21. David Platten reporter

    Reminder for myself:

    • Add code to set whether to delete the folder of images or not, much like for the other extractors
    • Remove Making explicit VR little endian part (isn't needed, slows things down)
    • Remove DICOMDIR (isn't needed, slows things down)
    • Check insertion of kVp data in to the initial RDSR
  22. David Platten reporter

    Removed two stages of the extractor as they are not needed: creating DICOMDIR, which in turn required all DICOM objects to be explicit VR little endian. This also reduces the time required to create the RDSR. I've also altered the method of inserting the kVp, as I found that the previous method failed when encountering multiple axial acquisitions that use the same exposure factors. References issue #480

    → <<cset 4bc2ee66b23a>>

  23. David Platten reporter

    This extractor also works for GE LightSpeed Plus studies that include a dose summary image, although no acquisition protocol names are obtained.

  24. Ed McDonagh

    Cool. Does it not get any protocol names? The pixelmed routine normally can get the overarching protocol name, like 5.13 abdomen blah blah, but not the series group names.

  25. David Platten reporter

    Hi Ed. For the LightSpeed Plus in OpenREM I have:

    • Accession number
    • Study date and time
    • Study description
    • Requested procedure (same as study description in this case)
    • Patient age
    • Hospital
    • Scanner make and model
    • Study UID
    • Total number of events
    • Total DLP

    For each scan component I have:

    • Type
    • CTDIvol
    • DLP
    • Scanning length
  26. David Platten reporter

    Updated Toshiba RDSR creation routine to combine multiple RDSRs together if there is more than one dose summary in the study. References issue #480

    → <<cset 055ec10621d5>>

  27. David Platten reporter

    I need to update the code that checks if there is more than one dose summary per study. At the moment it looks for multiple instances of series number 9000 (used by one of our Toshiba scanners). However, another of our Toshiba scanners uses series number 1000 for the dose summary objects, and a third scanner uses a series number in the normal range. This third system always has the text "SUMMARY" as part of the SeriesDescription, and this combined with an SOPClassUID of 1.2.840.10008.5.1.4.1.1.7 may make it identifiable as a dose summary.

  28. David Platten reporter

    Added SoftwareVersions and DeviceSerialNumber to the created RDSR. Now looking for Secondary Capture Image Storage objects when seeing if there is more than one study contained within each study uid. References issue #480

    → <<cset faad557c6588>>

  29. David Platten reporter

    Added code to check that the secondary capture object is a dose summary, and not some other type of secondary capture. I've done this because some virtual colonoscopy studies include surface rendered snapshots as secondary capture. References issue #480

    → <<cset b516d486edd5>>

  30. Ed McDonagh

    Hi @dplatten. Just wanted to check if this is still in future, or if it should be tidied up and documented for 0.8.0?

  31. David Platten reporter

    Started to update documentation for the new Toshiba RDSR creation extractor. Need to add info on how to install dcmtk, java.exe and pixelmed.jar. References issue #480

    → <<cset 59326ecf883c>>

  32. David Platten reporter

    Updated documentation a little to fix some layout issues. Need to add info on how to install dcmtk, java.exe and pixelmed.jar. References issue #480

    → <<cset 0064415f6501>>

  33. Ed McDonagh

    I need to think how these files are delivered to the user - I don't think I usually keep the stuff folder in the bundle that gets uploaded to pypi...

  34. Ed McDonagh

    I haven't reviewed your docs for this by the way - maybe you've covered it - apologies if you have!

  35. Ed McDonagh
    • changed status to open

    Reopen until the pull request is merged. I need to review the docs and make sure it makes sense to me.

  36. Ed McDonagh

    I keep getting a openrem_extractor.log file created in the openrem folder - and I can't see why. Any ideas?

  37. David Platten reporter

    Started to document Conquest configuration using lua, including forwarding Toshiba CT data to the RDSR creation importer. References issue #480

    → <<cset b8df0e4a4016>>

  38. Ed McDonagh

    Thanks for doing this @dplatten. One you might want to edit:

    "The above script depends on openrem_string_split are"

  39. Log in to comment