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