|
@@ -2,6 +2,7 @@ import slicer
|
|
import os
|
|
import os
|
|
import subprocess
|
|
import subprocess
|
|
import re
|
|
import re
|
|
|
|
+import dicom
|
|
|
|
|
|
dicomModify=os.getenv("HOME")
|
|
dicomModify=os.getenv("HOME")
|
|
if not dicomModify==None:
|
|
if not dicomModify==None:
|
|
@@ -36,6 +37,75 @@ class loadDicomLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
|
|
def __init__(self,parent):
|
|
def __init__(self,parent):
|
|
slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic.__init__(self, parent)
|
|
slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic.__init__(self, parent)
|
|
|
|
|
|
|
|
+ self.tag={
|
|
|
|
+ 'studyDate': "0008,0020",
|
|
|
|
+ 'studyTime': "0008,0030",
|
|
|
|
+ 'seriesTime':"0008,0031",
|
|
|
|
+ 'modality': "0008,0060",
|
|
|
|
+ 'presentationIntentType':"0008,0068",
|
|
|
|
+ 'manufacturer':"0008,0070",
|
|
|
|
+ 'studyDescription': "0008,1030",
|
|
|
|
+ 'seriesDescription': "0008,103e",
|
|
|
|
+ 'patientName':"0010,0010",
|
|
|
|
+ 'patientId':"0010,0020",
|
|
|
|
+ 'patientBirthDate': "0010,0030",
|
|
|
|
+ 'patientSex': "0010,0040",
|
|
|
|
+ 'patientAge': "0010,1010",
|
|
|
|
+ 'patientComments': "0010,4000",
|
|
|
|
+ 'sequenceName':"0018,0024",
|
|
|
|
+ 'kVP':"0018,0060",
|
|
|
|
+ 'percentPhaseFieldOfView':"0018,0094",
|
|
|
|
+ 'xRayTubeCurrent':"0018,1151",
|
|
|
|
+ 'exposure':"0018,1152",
|
|
|
|
+ 'imagerPixelSpacing':"0018,1164",
|
|
|
|
+ 'bodyPartThickness':"0018,11a0",
|
|
|
|
+ 'compressionForce':"0018,11a2",
|
|
|
|
+ 'viewPosition':"0018,5101",
|
|
|
|
+ 'fieldOfViewHorizontalFlip':"0018,7034",
|
|
|
|
+ 'studyInstanceUid':"0020,000d",
|
|
|
|
+ 'seriesInstanceUid':"0020,000e",
|
|
|
|
+ 'studyId': "0020,0010",
|
|
|
|
+ 'seriesNumber':"0020,0011",
|
|
|
|
+ 'instanceNumber':"0020,0013",
|
|
|
|
+ 'frameOfReferenceInstanceUid':"0020,0052",
|
|
|
|
+ 'imageLaterality':"0020,0060",
|
|
|
|
+ 'imagesInAcquisition':"0020,1002",
|
|
|
|
+ 'photometricInterpretation':"0028,0004"
|
|
|
|
+ }
|
|
|
|
+ self.tagPyDicom={
|
|
|
|
+ 'studyDate': 0x00080020,
|
|
|
|
+ 'studyTime': 0x00080030,
|
|
|
|
+ 'modality': 0x00080060,
|
|
|
|
+ 'presentationIntentType': 0x00080068,
|
|
|
|
+ 'manufacturer': 0x00080070,
|
|
|
|
+ 'studyDescription': 0x00081030,
|
|
|
|
+ 'seriesDescription': 0x0008103e,
|
|
|
|
+ 'patientName': 0x00100010,
|
|
|
|
+ 'patientId': 0x00100020,
|
|
|
|
+ 'patientBirthDate': 0x00100030,
|
|
|
|
+ 'patientSex': 0x00100040,
|
|
|
|
+ 'patientComments': 0x00104000,
|
|
|
|
+ 'sequenceName': 0x00180024,
|
|
|
|
+ 'kVP': 0x00180060,
|
|
|
|
+ 'percentPhaseFieldOfView': 0x00180094,
|
|
|
|
+ 'xRayTubeCurrent': 0x00181151,
|
|
|
|
+ 'exposure': 0x00181152,
|
|
|
|
+ 'imagerPixelSpacing': 0x00181164,
|
|
|
|
+ 'bodyPartThickness': 0x001811a0,
|
|
|
|
+ 'compressionForce': 0x001811a2,
|
|
|
|
+ 'viewPosition': 0x00185101,
|
|
|
|
+ 'studyInstanceUid': 0x0020000d,
|
|
|
|
+ 'seriesInstanceUid': 0x0020000e,
|
|
|
|
+ 'studyId': 0x00200010,
|
|
|
|
+ 'seriesNumber': 0x00200011,
|
|
|
|
+ 'instanceNumber': 0x00200013,
|
|
|
|
+ 'frameOfReferenceInstanceUid': 0x00200052
|
|
|
|
+ }
|
|
|
|
+ #new_dict_items={
|
|
|
|
+ # 0x001811a0: ('DS','BodyPartThickness','Body Part Thickness')
|
|
|
|
+ #}
|
|
|
|
+ #dicom.datadict.add_dict_entries(new_dict_items)
|
|
|
|
+
|
|
def load(self,sNet,dir,doRemove=True):
|
|
def load(self,sNet,dir,doRemove=True):
|
|
print("Loading dir {}").format(dir)
|
|
print("Loading dir {}").format(dir)
|
|
dicomFiles=sNet.listRelativeDir(dir)
|
|
dicomFiles=sNet.listRelativeDir(dir)
|
|
@@ -58,11 +128,13 @@ class loadDicomLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
|
|
|
|
|
|
for loadable in loadables:
|
|
for loadable in loadables:
|
|
#TODO check if it makes sense to load a particular loadable
|
|
#TODO check if it makes sense to load a particular loadable
|
|
|
|
+ fileBuffer = open(loadable.files[0])
|
|
|
|
+ plan=dicom.read_file(fileBuffer)
|
|
if loadable.name.find('imageOrientationPatient')>-1:
|
|
if loadable.name.find('imageOrientationPatient')>-1:
|
|
continue
|
|
continue
|
|
volumeNode=self.volumePlugin.load(loadable)
|
|
volumeNode=self.volumePlugin.load(loadable)
|
|
if volumeNode != None:
|
|
if volumeNode != None:
|
|
- vName='Series'+dicomValue(loadable.files[0],"0020,0011")
|
|
|
|
|
|
+ vName='Series'+plan[0x00200011].value
|
|
volumeNode.SetName(vName)
|
|
volumeNode.SetName(vName)
|
|
|
|
|
|
try:
|
|
try:
|
|
@@ -82,12 +154,149 @@ class loadDicomLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
|
|
for f in filelist:
|
|
for f in filelist:
|
|
os.remove(f)
|
|
os.remove(f)
|
|
|
|
|
|
|
|
+ def applyFilter(self,loadable,filter,nodeMetadata):
|
|
|
|
+
|
|
|
|
+ filterOK=True
|
|
|
|
+
|
|
|
|
+ fileBuffer = open(loadable.files[0])
|
|
|
|
+ try:
|
|
|
|
+ plan = dicom.read_file(fileBuffer)
|
|
|
|
+ except:
|
|
|
|
+ return False
|
|
|
|
+
|
|
|
|
+ for key in filter:
|
|
|
|
+ #try:
|
|
|
|
+ # fileValue=plan[self.tag[key]].value
|
|
|
|
+ #except KeyError:
|
|
|
|
+ # fileValue=None
|
|
|
|
+ fileValue=dicomValue(loadable.files[0],self.tag[key])
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if filter[key]=="SeriesLabel":
|
|
|
|
+ nodeMetadata['seriesLabel']=fileValue
|
|
|
|
+ continue
|
|
|
|
+
|
|
|
|
+ if not filter[key]==None:
|
|
|
|
+ if not fileValue==filter[key]:
|
|
|
|
+ print("File {} failed for tag {}: {}/{}").format(
|
|
|
|
+ loadable.files[0],key,fileValue,filter[key])
|
|
|
|
+ filterOK=False
|
|
|
|
+ break
|
|
|
|
+
|
|
|
|
+ nodeMetadata[key]=fileValue
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ return filterOK
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def loadVolumes(self,sNet,dir,filter,doRemove=True):
|
|
|
|
+ #returns all series from the directory, each as a separate node in a node list
|
|
|
|
+ #filter is a dictionary of speciifed dicom values, if filter(key)=None, that values
|
|
|
|
+ #get set, if it isn't, the file gets checked for a match
|
|
|
|
+
|
|
|
|
+ print("Loading dir {}").format(dir)
|
|
|
|
+ dicomFiles=sNet.listRelativeDir(dir)
|
|
|
|
+ #filelist=[os.path.join(dir,f) for f in os.listdir(dir)]
|
|
|
|
+ filelist=[]
|
|
|
|
+ for f in dicomFiles:
|
|
|
|
+ localPath=sNet.DownloadFileToCache(f)
|
|
|
|
+ f0=localPath
|
|
|
|
+ f1=f0+"1"
|
|
|
|
+ if not dicomModify==None:
|
|
|
|
+ subprocess.call(dicomModify+" "+f0+" "+f1+" && mv "+f1+" "+f0+";", shell=True)
|
|
|
|
+ filelist.append(localPath)
|
|
|
|
+
|
|
|
|
+ try:
|
|
|
|
+ loadables=self.volumePlugin.examineForImport([filelist])
|
|
|
|
+ except AttributeError:
|
|
|
|
+ self.volumePlugin=slicer.modules.dicomPlugins['DICOMScalarVolumePlugin']()
|
|
|
|
+ loadables=self.volumePlugin.examineForImport([filelist])
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ volumeNodes=[]
|
|
|
|
+ print("Number of loadables:{}").format(len(loadables))
|
|
|
|
+ for loadable in loadables:
|
|
|
|
+ #TODO check if it makes sense to load a particular loadable
|
|
|
|
+
|
|
|
|
+ print "Loading {} number of files: {}".format(loadable.name,len(loadable.files))
|
|
|
|
+ for f in loadable.files:
|
|
|
|
+ print "\t {}".format(f)
|
|
|
|
+ #perform checks
|
|
|
|
+ nodeMetadata={}
|
|
|
|
+ filterOK=self.applyFilter(loadable,filter,nodeMetadata)
|
|
|
|
+
|
|
|
|
+ if not filterOK:
|
|
|
|
+ #skip this loadable
|
|
|
|
+ continue
|
|
|
|
+
|
|
|
|
+ volumeNode=self.volumePlugin.load(loadable,"DCMTK")
|
|
|
|
+ if volumeNode != None:
|
|
|
|
+ vName='Series'+nodeMetadata['seriesLabel']
|
|
|
|
+ volumeNode.SetName(vName)
|
|
|
|
+ volume={'node':volumeNode,'metadata':nodeMetadata}
|
|
|
|
+ volumeNodes.append(volume)
|
|
|
|
+
|
|
|
|
+ if doRemove:
|
|
|
|
+ for f in filelist:
|
|
|
|
+ os.remove(f)
|
|
|
|
+
|
|
|
|
+ return volumeNodes
|
|
|
|
+
|
|
|
|
+ def loadSegmentations(self,net,dir,filter,doRemove=True):
|
|
|
|
+
|
|
|
|
+ print("Loading dir {}").format(dir)
|
|
|
|
+ dicomFiles=net.listRelativeDir(dir)
|
|
|
|
+ filelist=[net.DownloadFileToCache(f) for f in dicomFiles]
|
|
|
|
+ segmentationNodes=[]
|
|
|
|
+
|
|
|
|
+ try:
|
|
|
|
+ loadableRTs=self.RTPlugin.examineForImport([filelist])
|
|
|
|
+ except:
|
|
|
|
+ self.RTPlugin=plugin=slicer.modules.dicomPlugins['DicomRtImportExportPlugin']()
|
|
|
|
+ loadableRTs=self.RTPlugin.examineForImport([filelist])
|
|
|
|
+
|
|
|
|
+ for loadable in loadableRTs:
|
|
|
|
+
|
|
|
|
+ nodeMetadata={}
|
|
|
|
+ filterOK=self.applyFilter(loadable,filter,nodeMetadata)
|
|
|
|
+
|
|
|
|
+ if not filterOK:
|
|
|
|
+ continue
|
|
|
|
+
|
|
|
|
+ success=self.RTPlugin.load(loadable)
|
|
|
|
+
|
|
|
|
+ if not success:
|
|
|
|
+ print("Could not load RT structure set")
|
|
|
|
+ return
|
|
|
|
+
|
|
|
|
+ segNodes=slicer.util.getNodesByClass("vtkMRMLSegmentationNode")
|
|
|
|
+ segmentationNode=segNodes[0]
|
|
|
|
+ #assume we loaded the first node in list
|
|
|
|
+
|
|
|
|
+ if segmentationNode != None:
|
|
|
|
+ sName='Segmentation'+nodeMetadata['seriesLabel']
|
|
|
|
+ segmentationNode.SetName(sName)
|
|
|
|
+ segmentation={'node':segmentationNode,'metadata':nodeMetadata}
|
|
|
|
+ segmentationNodes.append(segmentation)
|
|
|
|
+
|
|
|
|
+ if not doRemove:
|
|
|
|
+ return segmentationNodes
|
|
|
|
+
|
|
|
|
+ for f in filelist:
|
|
|
|
+ os.remove(f)
|
|
|
|
+
|
|
|
|
+ return segmentationNodes
|
|
|
|
+
|
|
def dicomValue(file,tag):
|
|
def dicomValue(file,tag):
|
|
dcmdump=os.path.join(os.environ['SLICER_HOME'],"bin")
|
|
dcmdump=os.path.join(os.environ['SLICER_HOME'],"bin")
|
|
dcmdump=os.path.join(dcmdump,"dcmdump")
|
|
dcmdump=os.path.join(dcmdump,"dcmdump")
|
|
try:
|
|
try:
|
|
out=subprocess.check_output([dcmdump,'+P',tag,file])
|
|
out=subprocess.check_output([dcmdump,'+P',tag,file])
|
|
out=re.sub(r'^.*\[(.*)\].*\n$',r'\1',out)
|
|
out=re.sub(r'^.*\[(.*)\].*\n$',r'\1',out)
|
|
|
|
+ #separate out series
|
|
|
|
+ if out.find('\\')>-1:
|
|
|
|
+ out=out.split('\\')
|
|
|
|
+
|
|
return out
|
|
return out
|
|
except:
|
|
except:
|
|
return None
|
|
return None
|