import dicom, SetBeam20, scaleConverter
from xml.etree import ElementTree as ET
import numpy as np
#from utils import SetBeam20
from . import SetBeam20
# Author: Pankaj Mishra, Jul 7, 2014
# Update: 7/29/2014 Removed hardcoded path -CLW
# Fixed mm to cm conversion error - CLW 10/24/2014, see #3
[docs]class Dicom2Xml:
def __init__(self, filename, outxml):
self.filename = filename
self.outxml = outxml
def dicomToDataset(self):
dataset = dicom.read_file(self.filename, force=True)
self.ds = dataset
[docs] def getLeafJaws(self, leafJawsSequence, cp):
''' Extract Mlc, X or Y jaws (if present)
and then add it to the existinXML string
'''
selectionStr = {"MLCX": "Mlc", "ASYMX": "X", "ASYMY": "Y", "X": "X", "Y": "Y"}
for lj in leafJawsSequence:
if(selectionStr[lj.RTBeamLimitingDeviceType] == "Y"):
# Fixed mm to cm conversion error - CLW 10/24/2014
ET.SubElement(cp, 'Y1').text = str(lj.LeafJawPositions[0]/10.0)
ET.SubElement(cp, 'Y2').text = str(lj.LeafJawPositions[1]/10.0)
elif(selectionStr[lj.RTBeamLimitingDeviceType] == "X"):
# Fixed mm to cm conversion error - CLW 10/24/2014
ET.SubElement(cp, 'X1').text = str(lj.LeafJawPositions[0]/10.0)
ET.SubElement(cp, 'X2').text = str(lj.LeafJawPositions[1]/10.0)
elif(selectionStr[lj.RTBeamLimitingDeviceType] == "Mlc"):
mlcTag = ET.SubElement(cp,'Mlc')
ET.SubElement(mlcTag,'ID').text = '1'
# Fixed mm to cm conversion error - CLW 10/24/2014
Barr=map(lambda x:float(x)/10.0,lj.LeafJawPositions[:60])
Aarr=map(lambda x:float(x)/10.0,lj.LeafJawPositions[60:])
ET.SubElement(mlcTag, 'B').text = " ".join(map(str,Barr))
ET.SubElement(mlcTag, 'A').text = " ".join(map(str,Aarr))
[docs] def findMlcModel(self, inMlc):
'''
Determine the MLCModel type. Currently NDS120, NDS120HD, NDS80 are supported.
:param inMlc: MLC array from the DICOM file
'''
# MLC model pattern NDS120 (in mm)
firstLast10 = np.array(10*np.ones(10, dtype='float', order = 'C'))
middle40 = np.array(5*np.ones(40, dtype='float', order = 'C'))
nds120 = np.concatenate((firstLast10, middle40, firstLast10))
# MLC model pattern ND120HD (in mm)
firstLast14 = np.array(5*np.ones(14, dtype=np.float32, order = 'C'))
middle32 = np.array(2.5*np.ones(32, dtype=np.float32, order = 'C'))
nds120HD = np.concatenate((firstLast14, middle32, firstLast14))
# Now some constants
mlcLength = len(inMlc)
if mlcLength == 61:
if np.all(np.diff(inMlc) == nds120):
return 'NDS120'
elif np.all(np.diff(inMlc) == nds120HD):
return 'NDS120HD'
elif mlcLength == 41:
return 'NDS80'
def extractControlPoints(self):
lookupTable = {'NominalBeamEnergy' : 'Energy', \
'DoseRateSet' : 'DRate', \
'GantryAngle' : 'GantryRtn', \
'BeamLimitingDeviceAngle' : 'CollRtn',\
'TableTopVerticalPosition' : 'CouchVrt',\
'TableTopLongitudinalPosition' : 'CouchLng', \
'TableTopLateralPosition' : 'CouchLat',\
'PatientSupportAngle' : 'CouchRtn',\
'CumulativeMetersetWeight' : 'Mu',\
'BeamLimitingDevicePositionSequence' : 'MlcJaws'
}
energyType = {"PHOTON": "x", "ELECTRON": "e"}
dose = 0;
root = ET.Element('VarianResearchBeam')
sb = ET.SubElement(root,'SetBeam')
ET.SubElement(sb, 'Id').text = '1'
# Determine the MLCModel type: NDS80, NDS120, NDS120HD
inMlc = [float(s) for s in self.ds.BeamSequence[0].BeamLimitingDeviceSequence[2].LeafPositionBoundaries]
ET.SubElement(sb, 'MLCModel').text = self.findMlcModel(inMlc)
ET.SubElement(sb, 'Accs').text = ''
controlpoint = ET.SubElement(sb, 'ControlPoints')
dosePrev = 0.0 # For cumulative dose
for (beam, frac) in zip(self.ds.BeamSequence, self.ds.FractionGroupSequence[0].ReferencedBeamSequence):
# Control point
for cnt in range(0, beam.NumberofControlPoints):
cp = ET.SubElement(controlpoint, 'Cp')
for elem in beam.ControlPointSequence[cnt]:
keyTag = elem.name.replace(" ", "")
if(keyTag in lookupTable.keys()):
if (lookupTable[keyTag] == 'Energy'):
ET.SubElement(cp, lookupTable[keyTag]).text = str(elem.value) + energyType[beam.RadiationType]
elif (lookupTable[keyTag] == "MlcJaws"):
self.getLeafJaws(elem, cp)
elif(lookupTable[keyTag] == "Mu"):
dose = dosePrev + frac.BeamMeterset*elem.value
ET.SubElement(cp, lookupTable[keyTag]).text = str(dose)
else:
ET.SubElement(cp, lookupTable[keyTag]).text = str(elem.value)
dosePrev = dosePrev + frac.BeamMeterset # Cumulative dose
#------------------------------------ Validate and write output XML file
# Convert IEC (from DICOM) to Varian Scale
# NOTE: Please fix the schema check part SOOOOON!!!!!
tree = ET.ElementTree(root)
tree.write(self.outxml)
fp = scaleConverter.ScaleConverter(self.outxml)
fp.convertScale('VarianScale')
fp.tree.write(self.outxml)