|
@@ -1,360 +0,0 @@
|
|
|
-import DICOMLib
|
|
|
-from slicer.ScriptedLoadableModule import *
|
|
|
-import slicerNetwork
|
|
|
-import qt,vtk,ctk,slicer
|
|
|
-import datetime
|
|
|
-import re
|
|
|
-import os
|
|
|
-import slicer.cli
|
|
|
-
|
|
|
-class exportDicom(slicer.ScriptedLoadableModule.ScriptedLoadableModule):
|
|
|
- def __init__(self,parent):
|
|
|
- slicer.ScriptedLoadableModule.ScriptedLoadableModule.__init__(self, parent)
|
|
|
- self.className="exportDicom"
|
|
|
- self.parent.title="exportDicom"
|
|
|
- self.parent.categories = ["Examples"]
|
|
|
- self.parent.dependencies = []
|
|
|
- self.parent.contributors = ["Andrej Studen (University of Ljubljana)"] # replace with "Firstname Lastname (Organization)"
|
|
|
- self.parent.helpText = """
|
|
|
- This is an example of scripted loadable module bundled in an extension.
|
|
|
- It adds hierarchy datat for reliable dicom export of a node
|
|
|
- """
|
|
|
- self.parent.helpText += self.getDefaultModuleDocumentationLink()
|
|
|
- self.parent.acknowledgementText = """
|
|
|
- This extension developed within Medical Physics research programe of ARRS
|
|
|
- """ # replace with organization, grant and thanks.
|
|
|
-
|
|
|
-#
|
|
|
-# dataExplorerWidget
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-class exportDicomWidget(ScriptedLoadableModuleWidget):
|
|
|
- """Uses ScriptedLoadableModuleWidget base class, available at:
|
|
|
- https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
|
|
|
- """
|
|
|
-
|
|
|
- def setup(self):
|
|
|
- ScriptedLoadableModuleWidget.setup(self)
|
|
|
- self.logic=exportDicomLogic(self)
|
|
|
-
|
|
|
- try:
|
|
|
- fhome=os.environ["HOME"]
|
|
|
- except:
|
|
|
- #in windows, the variable is called HOMEPATH
|
|
|
- fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
|
|
|
-
|
|
|
- cfgPath=os.path.join(fhome,".labkey")
|
|
|
- cfgPath=os.path.join(cfgPath,"onko-nix.json")
|
|
|
-
|
|
|
- self.onkoNet=slicerNetwork.labkeyURIHandler()
|
|
|
- self.onkoNet.parseConfig(cfgPath)
|
|
|
- self.onkoNet.initRemote()
|
|
|
-
|
|
|
- self.project='EMBRACE/Studija'
|
|
|
-
|
|
|
- datasetCollapsibleButton = ctk.ctkCollapsibleButton()
|
|
|
- datasetCollapsibleButton.text = "Node data"
|
|
|
- self.layout.addWidget(datasetCollapsibleButton)
|
|
|
- # Layout within the dummy collapsible button
|
|
|
- datasetFormLayout = qt.QFormLayout(datasetCollapsibleButton)
|
|
|
-
|
|
|
- self.volumeNode=qt.QLineEdit("Shifted_1_PTV N")
|
|
|
- datasetFormLayout.addRow("volumeNode:",self.volumeNode)
|
|
|
- self.patientId=qt.QLineEdit("LJU004")
|
|
|
- datasetFormLayout.addRow("PatientId:",self.patientId)
|
|
|
- self.studyInstanceUid=qt.QLineEdit("1.2.840.113704.1.1762661776.922.1250707615.8")
|
|
|
- datasetFormLayout.addRow("StudyInstanceUid:",self.studyInstanceUid)
|
|
|
- self.studyDescription=qt.QLineEdit("Segmentation:PTV N")
|
|
|
- datasetFormLayout.addRow("StudyDescription:",self.studyDescription)
|
|
|
-
|
|
|
- self.addHierarchyButton=qt.QPushButton("Add hierarchy")
|
|
|
- self.addHierarchyButton.clicked.connect(self.onAddHierarchyButtonClicked)
|
|
|
- datasetFormLayout.addRow("Volume:",self.addHierarchyButton)
|
|
|
-
|
|
|
- self.exportButton=qt.QPushButton("Export")
|
|
|
- self.exportButton.clicked.connect(self.onExportButtonClicked)
|
|
|
- datasetFormLayout.addRow("Export:",self.exportButton)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- def onAddHierarchyButtonClicked(self):
|
|
|
- metadata={'patientId':self.patientId.text,
|
|
|
- 'studyDescription':self.studyDescription.text,
|
|
|
- 'studyInstanceUid':self.studyInstanceUid.text}
|
|
|
- node=slicer.util.getFirstNodeByName(self.volumeNode.text)
|
|
|
- if node==None:
|
|
|
- return
|
|
|
- self.logic.addHierarchy(node,metadata)
|
|
|
-
|
|
|
- def onExportButtonClicked(self):
|
|
|
- metadata={'patientId':self.patientId.text,
|
|
|
- 'studyDescription':self.studyDescription.text,
|
|
|
- 'studyInstanceUid':self.studyInstanceUid.text,
|
|
|
- 'seriesInstanceUid':self.logic.generateSeriesUUID('volume'),
|
|
|
- 'frameOfReferenceInstanceUid':self.logic.generateFrameOfReferenceUUID('volume')}
|
|
|
- node=slicer.util.getFirstNodeByName(self.volumeNode.text)
|
|
|
- if node==None:
|
|
|
- return
|
|
|
- self.logic.exportNode(self.onkoNet,self.project,node,metadata)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-class exportDicomLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
|
|
|
- def __init__(self,parent):
|
|
|
- slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic.__init__(self, parent)
|
|
|
- self.baseUUID='1.2.826.0.1.36800.43.10.248'
|
|
|
- self.labelUUID={'series':'1','study':'2','instance':'3','frameOfReference':4}
|
|
|
- self.dataUUID={'volume':'1','segmentation':'2','transformation':'3'}
|
|
|
-
|
|
|
- try:
|
|
|
- fhome=os.environ["HOME"]
|
|
|
- except:
|
|
|
- #in windows, the variable is called HOMEPATH
|
|
|
- fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
|
|
|
-
|
|
|
-
|
|
|
- self.basePath=os.path.join(fhome,'.dicom')
|
|
|
-
|
|
|
- if not os.path.isdir(self.basePath):
|
|
|
- os.mkdir(self.basePath)
|
|
|
-
|
|
|
- self.itemLabel={
|
|
|
- 'patientName': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientNameAttributeName(),
|
|
|
- 'modality': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMSeriesModalityAttributeName(),
|
|
|
- 'patientId': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientIDAttributeName(),
|
|
|
- 'patientSex': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientSexAttributeName(),
|
|
|
- 'patientBirthDate': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientBirthDateAttributeName(),
|
|
|
- 'patientComments': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientCommentsAttributeName(),
|
|
|
- 'studyDescription': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyDescriptionAttributeName(),
|
|
|
- 'studyInstanceUid': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyInstanceUIDTagName(),
|
|
|
- 'studyDate': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyDateAttributeName(),
|
|
|
- 'studyId': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyIDTagName(),
|
|
|
- 'studyTime': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyTimeAttributeName()}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- def generateStudyUUID(self,type):
|
|
|
-
|
|
|
- x=datetime.datetime.now()
|
|
|
- date=x.strftime("%Y%m%d")
|
|
|
- studyFile=os.path.join(self.basePath,'studyCount'+date+'.txt')
|
|
|
-
|
|
|
- try:
|
|
|
- f=open(studyFile,"r")
|
|
|
- id=int(f.readline())
|
|
|
- id=id+1
|
|
|
- f.close()
|
|
|
- except:
|
|
|
- id=0
|
|
|
-
|
|
|
- studyId="{}.{}.{}.{}.{}".format(self.baseUUID,self.labelUUID['study'],
|
|
|
- self.dataUUID[type],date,id)
|
|
|
-
|
|
|
- f=open(studyFile,"w")
|
|
|
- f.write("{}".format(id))
|
|
|
- f.close()
|
|
|
- return studyId
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- def generateFrameOfReferenceUUID(self,type):
|
|
|
-
|
|
|
- x=datetime.datetime.now()
|
|
|
- date=x.strftime("%Y%m%d")
|
|
|
- forFile=os.path.join(self.basePath,'frameCount'+date+'.txt')
|
|
|
-
|
|
|
- try:
|
|
|
- f=open(studyFile,"r")
|
|
|
- id=int(f.readline())
|
|
|
- id=id+1
|
|
|
- f.close()
|
|
|
- except:
|
|
|
- id=0
|
|
|
-
|
|
|
- forId="{}.{}.{}.{}.{}".format(self.baseUUID,self.labelUUID['frameOfReference'],
|
|
|
- self.dataUUID[type],date,id)
|
|
|
-
|
|
|
- f=open(forFile,"w")
|
|
|
- f.write("{}".format(id))
|
|
|
- f.close()
|
|
|
- return forId
|
|
|
-
|
|
|
- def generateSeriesUUID(self,type):
|
|
|
-
|
|
|
- x=datetime.datetime.now()
|
|
|
- hour=x.strftime("%H")
|
|
|
- hour=re.sub('^0','',hour)
|
|
|
- ft=hour+x.strftime("%M%S")
|
|
|
- seriesInstanceUid=self.baseUUID+'.'+self.labelUUID['series']+'.'
|
|
|
- seriesInstanceUid+=self.dataUUID[type]+'.'+x.strftime("%Y%m%d")+'.'+ft
|
|
|
- return seriesInstanceUid
|
|
|
-
|
|
|
- def setAttribute(self,shn,itemId,label,metadata):
|
|
|
- try:
|
|
|
- shn.SetItemAttribute(itemId,self.itemLabel['modality'],metadata['modality'])
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
-
|
|
|
- def addHierarchy(self,dataNode,metadata):
|
|
|
- #convert dataNode to fully fledged DICOM object in hierarchy
|
|
|
-
|
|
|
- #variation of addSeriesInSubjectHierarchy
|
|
|
- shn=slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
|
|
|
- sceneItemId=shn.GetSceneItemID()
|
|
|
-
|
|
|
- #add the series for the data node
|
|
|
- seriesItemId=shn.CreateItem(sceneItemId,dataNode)
|
|
|
-
|
|
|
- x=datetime.datetime.now()
|
|
|
- hour=x.strftime("%H")
|
|
|
- hour=re.sub('^0','',hour)
|
|
|
- ft=hour+x.strftime("%M%S")
|
|
|
- seriesInstanceUid=self.baseUUID+'.'+self.labelUUID['series']+'.'
|
|
|
- seriesInstanceUid+=self.dataUUID['volume']+'.'+x.strftime("%Y%m%d")+'.'+ft
|
|
|
- shn.SetItemUID(seriesItemId,slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(),
|
|
|
- seriesInstanceUid)
|
|
|
-
|
|
|
- self.setAttribute(shn,seriesItemId,'modality',metadata)
|
|
|
- self.setAttribute(shn,seriesItemId,'seriesNumber',metadata)
|
|
|
-
|
|
|
- #add PatientId
|
|
|
- try:
|
|
|
- patientId=metadata['patientId']
|
|
|
- except KeyError:
|
|
|
- patientId='Unknown'
|
|
|
-
|
|
|
- try:
|
|
|
- studyInstanceUid=metadata['studyInstanceUid']
|
|
|
- except KeyError:
|
|
|
- studyInstanceUid=self.generateStudyUUID('volume')
|
|
|
-
|
|
|
- slicer.vtkSlicerSubjectHierarchyModuleLogic.InsertDicomSeriesInHierarchy(shn,patientId,
|
|
|
- studyInstanceUid,seriesInstanceUid)
|
|
|
-
|
|
|
- patientItemId=shn.GetItemByUID(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(),patientId)
|
|
|
-
|
|
|
- self.setAttribute(shn,patientItemId,'patientName',metadata)
|
|
|
- self.setAttribute(shn,patientItemId,'patientId',metadata)
|
|
|
- self.setAttribute(shn,patientItemId,'patientSex',metadata)
|
|
|
- self.setAttribute(shn,patientItemId,'patientBirthDate',metadata)
|
|
|
- self.setAttribute(shn,patientItemId,'patientComments',metadata)
|
|
|
-
|
|
|
- patientItemName=patientId
|
|
|
- shn.SetItemName(patientItemId,patientItemName)
|
|
|
-
|
|
|
- studyItemId=shn.GetItemByUID(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(),studyInstanceUid)
|
|
|
-
|
|
|
- self.setAttribute(shn,studyItemId,'studyDescription',metadata)
|
|
|
- self.setAttribute(shn,studyItemId,'studyInstanceUid',metadata)
|
|
|
- self.setAttribute(shn,studyItemId,'studyId',metadata)
|
|
|
- self.setAttribute(shn,studyItemId,'studyDate',metadata)
|
|
|
- self.setAttribute(shn,studyItemId,'studyTime',metadata)
|
|
|
-
|
|
|
- studyItemName=studyInstanceUid
|
|
|
- shn.SetItemName(studyItemId,studyItemName)
|
|
|
-
|
|
|
- def setTagValue(self,cliparameters,metadata,field):
|
|
|
- try:
|
|
|
- cliparameters[field]=metadata[field]
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
-
|
|
|
- def setDirectoryValue(self,cliparameters,cliKey,metadata,key):
|
|
|
- try:
|
|
|
- cliparameters[cliKey]=metadata[key]
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- def generateDicom(self, volumeNode, metadata, fdir):
|
|
|
-
|
|
|
- cliparameters={}
|
|
|
- tagList=['patientName','patientComments','studyDate','studyTime','studyDescription',
|
|
|
- 'modality','manufacturer','model','seriesDescription',
|
|
|
- 'seriesNumber','seriesDate','seriesTime','contentDate','contentTime']
|
|
|
-
|
|
|
- for tag in tagList:
|
|
|
- self.setTagValue(cliparameters,metadata,tag)
|
|
|
-
|
|
|
- valuePairs={'patientID':'patientId',
|
|
|
- 'studyID':'studyId',
|
|
|
- 'seriesInstanceUID':'seriesInstanceUid',
|
|
|
- 'studyInstanceUID':'studyInstanceUid',
|
|
|
- 'frameOfReferenceInstanceUID':'frameOfReferenceInstanceUid'}
|
|
|
-
|
|
|
- for key in valuePairs:
|
|
|
- self.setDirectoryValue(cliparameters,key,metadata,valuePairs[key])
|
|
|
-
|
|
|
-
|
|
|
- cliparameters['dicomDirectory']=fdir
|
|
|
- cliparameters['dicomPrefix']='IMG'
|
|
|
- cliparameters['inputVolume']=volumeNode.GetID()
|
|
|
-
|
|
|
- print("[CLI]SeriesInstanceUID: {}").format(cliparameters['seriesInstanceUID'])
|
|
|
- print("[MeD]SeriesInstanceUID: {}").format(metadata['seriesInstanceUid'])
|
|
|
-
|
|
|
- try:
|
|
|
- dicomWrite=slicer.modules.createdicomseries
|
|
|
- except AttributeError:
|
|
|
- print("Missing dicom exporter")
|
|
|
- return
|
|
|
-
|
|
|
- cliNode=slicer.cli.run(dicomWrite,None,cliparameters,wait_for_completion=True)
|
|
|
- status=cliNode.GetStatusString()
|
|
|
- print("Status: {}").format(status)
|
|
|
- if status.find("error")>-1:
|
|
|
- print("Error: {}").format(cliNode.GetErrorText())
|
|
|
- return False
|
|
|
- return True
|
|
|
-
|
|
|
-
|
|
|
- def exportNode(self,net, project,volumeNode,metadata):
|
|
|
-
|
|
|
-
|
|
|
- #buffed up exportable is now ready to be stored
|
|
|
- fdir=net.GetLocalCacheDirectory()
|
|
|
- project=project.replace('/','\\')
|
|
|
- fdir=os.path.join(fdir,project)
|
|
|
- fdir=os.path.join(fdir,'%40files')
|
|
|
- fdir=os.path.join(fdir,metadata['patientId'])
|
|
|
- fdir=os.path.join(fdir,'Registration')
|
|
|
-
|
|
|
- if not os.path.isdir(fdir):
|
|
|
- os.mkdir(fdir)
|
|
|
-
|
|
|
- relDir=net.GetRelativePathFromLocalPath(fdir)
|
|
|
- remoteDir=net.GetLabkeyPathFromRelativePath(relDir)
|
|
|
-
|
|
|
- if not net.isRemoteDir(remoteDir):
|
|
|
- net.mkdir(remoteDir)
|
|
|
-
|
|
|
- dirName=volumeNode.GetName();
|
|
|
- dirName=dirName.replace(" ","_")
|
|
|
- fdir=os.path.join(fdir,dirName)
|
|
|
-
|
|
|
- if not os.path.isdir(fdir):
|
|
|
- os.mkdir(fdir)
|
|
|
-
|
|
|
- relDir=net.GetRelativePathFromLocalPath(fdir)
|
|
|
- remoteDir=net.GetLabkeyPathFromRelativePath(relDir)
|
|
|
-
|
|
|
- if not net.isRemoteDir(remoteDir):
|
|
|
- net.mkdir(remoteDir)
|
|
|
-
|
|
|
- if not self.generateDicom(volumeNode,metadata,fdir):
|
|
|
- return
|
|
|
-
|
|
|
- for f in os.listdir(fdir):
|
|
|
- localPath=os.path.join(fdir,f)
|
|
|
- print("localPath: {}").format(localPath)
|
|
|
- relativePath=net.GetRelativePathFromLocalPath(localPath)
|
|
|
- print("relativePath: {}").format(relativePath)
|
|
|
- remotePath=net.GetLabkeyPathFromRelativePath(relativePath)
|
|
|
- print("remotePath: {}").format(relativePath)
|
|
|
- net.copyLocalFileToRemote(localPath,remotePath)
|