Overlaying calculated/measured DVHs

219 views
Skip to first unread message

Kostya Golubev

unread,
Jul 14, 2014, 6:06:19 AM7/14/14
to dicom...@googlegroups.com
Dear dicomplyler users/developers!

I want to use dicompyler to overlay calculated/measured DVHs. I let dicompyler calculate DVHs from RTD/RTS exported from Pinnacle (Pinnacle does not export DVHs). Measured DVHs come from the Octavius phantom software as a dicom file. The following standalone python code works fine:

<code begin>

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

def myGenerateCDVH(data):
    """Generate a cumulative DVH (cDVH) from a differential DVH (dDVH)"""

    dDVH = np.array(data)
    # Separate the dose and volume values into distinct arrays
    dose = data[0::2]
    volume = data[1::2]

    # Get the min and max dose and volume values
    mindose = int(dose[0]*100)
    maxdose = int(sum(dose)*100)
    maxvol = sum(volume)

    # Determine the dose values that are missing from the original data
    missingdose = np.ones(mindose) * maxvol

    # Generate the cumulative dose and cumulative volume data
    k = 0
    cumvol = []
    cumdose = []
    while k < len(dose):
        cumvol += [volume[k]]              # here is the only difference from the standard GenerateCDVH
        cumdose += [sum(dose[:k])]
        k += 1
    cumvol = np.array(cumvol)
    cumdose = np.array(cumdose)*100

    # Interpolate the dDVH data for 1 cGy bins
    interpdose = np.arange(mindose, maxdose+1)
    interpcumvol = np.interp(interpdose, cumdose, cumvol)

    # Append the interpolated values to the missing dose values
    cumDVH = np.append(missingdose, interpcumvol)

    return cumDVH




def main():

    ds = dicom.read_file( "C:\dicom\dvh\dvhs.dcm" )

    plt.figure()
    plt.grid(True)
   
    for item in ds.DVHs:
      refROINum=item.DVHReferencedROIs[0].ReferencedROINumber
      if 15 == refROINum:  # say the measured DVH for structure 15 is available
        dvhitem = {}
        dvhitem['data'] = myGenerateCDVH(item.DVHData)
        plt.plot(dvhitem['data'])

    plt.show()


if __name__ == "__main__":
    main()

<code end>

The only difference between myGenerateCDVH and GenerateCDVH is

cumvol += [sum(volume[k:])] -> cumvol += [sum(volume[k])]

as Octavius software stores cumulated volume.

Now, I try to integrate this into dicomplyler:

1) I define a shoft plugin to store the path to the DVHs dicom in globally available config.pathToDVH variable.
2) I want to open this file onStructureSelect in DVHs tab, check whether corresponding measured DVH is available and plot it on top of already plotted  DVHs. I extend OnStructureSelect in dvh.py as follows:

 """Load the constraints for the currently selected structure."""
       
        if (msg.data['id'] == None):
            self.EnableConstraints(False)
        else:
            print "*** structure selected, id: ", self.structureid
            print "pathToDVH: ", config.pathToDVH

            if not (config.pathToDVH=="none"):

              """ Open DVH dicom """
              ds = dicom.read_file(config.pathToDVH)
              print "RTS file meta ", ds.file_meta
                        
              print "DVHSequnce length: ", len(ds.DVHs)
              for item in ds.DVHs:
                refROINum=item.DVHReferencedROIs[0].ReferencedROINumber
                if self.structureid == refROINum:
                    print "ref roi num ", refROINum, " found"
                  
                    dvhitem = {}
                    dvhitem['data'] = self.myGenerateCDVH(item.DVHData)
                    fig = self.guiDVH.panelDVH.get_figure()
                    axes = fig.gca()
                  
                    print "axes:", axes,
                    print " isaxes: ", isinstance(axes, matplotlib.axes.Axes)
                  
                    color = np.zeros(3)
                    maxlen = 10000
                    self.guiDVH.DrawDVH(dvhitem,
                                self.structures[self.structureid],
                                axes, color, maxlen)

<the rest unchanged>
self.structureid = msg.data['id']

Here's the output:

*** structure selected, id:  7
pathToDVH:  C:\dicom\dvh\dvhs.dcm
RTS file meta  (0002, 0001) File Meta Information Version       OB: '\x00\x01'
(0002, 0002) Media Storage SOP Class UID         UI: RT Dose Storage
(0002, 0003) Media Storage SOP Instance UID      UI: 1.2.276.0.105.100.1400686661361266.1400686661330066.101.4
(0002, 0010) Transfer Syntax UID                 UI: Implicit VR Little Endian
(0002, 0012) Implementation Class UID            UI: 1.2.276.0.7230010.3.0.3.6.0
(0002, 0013) Implementation Version Name         SH: 'OFFIS_DCMTK_360'
DVHSequnce length:  7
ref roi num  7  found
axes: Axes(0.125,0.1;0.775x0.8)  isaxes:  True
ERROR: Unhandled exception: Traceback (most recent call last):
  File "J:\soft\dicompyler-source\dicompyler\guiutil.py", line 229, in OnCheck
    pub.sendMessage('colorcheckbox.checked.' + self.pubsubname, message)
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 750, in sendMessage
    self.__topicTree.sendMessage(aTopic, message, onTopicNeverCreated)
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 423, in sendMessage
    deliveryCount += node.sendMessage(message)
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 261, in sendMessage
    listener(message)
  File "J:\soft\dicompyler-source\dicompyler\main.py", line 591, in OnStructureCheck
    self.OnStructureSelect()
  File "J:\soft\dicompyler-source\dicompyler\main.py", line 638, in OnStructureSelect
    pub.sendMessage('structure.selected', {'id':id})
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 750, in sendMessage
    self.__topicTree.sendMessage(aTopic, message, onTopicNeverCreated)
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 423, in sendMessage
    deliveryCount += node.sendMessage(message)
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 261, in sendMessage
    listener(message)
  File "J:\soft\dicompyler-source\dicompyler\baseplugins\dvh.py", line 355, in OnStructureSelect
    axes, color, maxlen)
  File "J:\soft\dicompyler-source\dicompyler\guidvh.py", line 93, in DrawDVH
    linestyle=linestyle)
  File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 3849, in plot
    self.add_line(line)
  File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 1443, in add_line
    self._update_line_limits(line)
  File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 1451, in _update_line_limits
    p = line.get_path()
  File "C:\Python27\lib\site-packages\matplotlib\lines.py", line 644, in get_path
    self.recache()
  File "C:\Python27\lib\site-packages\matplotlib\lines.py", line 401, in recache
    y = np.asarray(yconv, np.float_)
  File "C:\Python27\lib\site-packages\numpy\core\numeric.py", line 235, in asarray
    return array(a, dtype, copy=False, order=order)
TypeError: float() argument must be a string or a number


I'm actually new to python and matplotlib, so at this point I got lost in the debries of matplotlib. Any help is greatly appreciated. Btw, such little functionality extension may be usefull for others, what do you think?


Thanks in advance,

Kostya

Adit Panchal

unread,
Jul 27, 2014, 11:50:37 PM7/27/14
to dicom...@googlegroups.com
Dear Kostya,

Sorry for the delayed response.

First, this is would be a great and useful addition to dicompyler. You are on the right track.

Your last line:

self.guiDVH.DrawDVH(dvhitem,
                                self.structures[self.structureid],
                                axes, color, maxlen)

Should be:

self.guiDVH.DrawDVH(dvhitem['data'] * 100 / dvhitem['data'][0],

                                self.structures[self.structureid],
                                axes, color, maxlen)

Since you are manually plotting the DVH, you need to convert the data to relative volume cumulative DVH. This is done automatically in the dvh.py for the loaded DVHs on line 157, which uses dvhdata.py.

Hope this helps,

Adit


--
-- You received this message because you are subscribed to the Google Groups dicompyler group. To post to this group, send email to dicom...@googlegroups.com. To unsubscribe from this group, send email to dicompyler+...@googlegroups.com. For more options, visit this group at https://groups.google.com/d/forum/dicompyler?hl=en
---
You received this message because you are subscribed to the Google Groups "dicompyler" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dicompyler+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages