123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- import DICOMLib
- from slicer.ScriptedLoadableModule import *
- import slicerNetwork
- import qt,vtk,ctk,slicer
- import datetime
- import re
- import os
- import slicer.cli
- import json
- 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 = ["LabKey"]
- 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")
- cfgNet=os.path.join(cfgPath,"Remote.json")
- self.project='DICOM/Export'
-
- baseUUIDFile=os.path.join(cfgPath,"baseUUID.json")
- baseUUIDCandidate='0.0.0.0.0'
- try:
- print("Opening {}".format(baseUUIDFile))
- f=open(baseUUIDFile,'r')
- print("Parsing {}".format(baseUUIDFile))
- baseUUIDCfg=json.load(f)
- print("Decoding {}".format(baseUUIDCfg))
- baseUUIDCandidate=baseUUIDCfg['baseUUID']
- print("baseUUID {}".format(baseUUIDCandidate))
- self.project=baseUUIDCfg['project']
- print("project {}".format(self.project))
-
- except IOError:
- pass
- except JSONDecodeError:
- pass
- except KeyError:
- pass
-
-
- self.Net=slicerNetwork.labkeyURIHandler()
- self.Net.parseConfig(cfgNet)
- self.Net.initRemote()
-
- 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("enter name of the volume Node")
- datasetFormLayout.addRow("volumeNode:",self.volumeNode)
- self.patientId=qt.QLineEdit("enter PatientID")
- datasetFormLayout.addRow("PatientId:",self.patientId)
- self.studyInstanceUid=qt.QLineEdit("enter study id")
- datasetFormLayout.addRow("StudyInstanceUid:",self.studyInstanceUid)
- self.studyDescription=qt.QLineEdit("enter study description")
- datasetFormLayout.addRow("StudyDescription:",self.studyDescription)
- self.addHierarchyButton=qt.QPushButton("Add hierarchy")
- self.addHierarchyButton.clicked.connect(self.onAddHierarchyButtonClicked)
- datasetFormLayout.addRow("Volume:",self.addHierarchyButton)
-
- self.baseUUIDText=qt.QLineEdit(baseUUIDCandidate)
- datasetFormLayout.addRow("Base UUID:",self.baseUUIDText)
-
-
- 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,
- 'baseUUID':self.baseUUIDText.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',self.baseUUIDText.text),
- 'frameOfReferenceInstanceUid':self.logic.generateFrameOfReferenceUUID('volume',self.baseUUIDText.text)}
- node=slicer.util.getFirstNodeByName(self.volumeNode.text)
- if node==None:
- return
- self.logic.exportNodeAsDICOM(self.Net,self.project,node,metadata)
- class exportDicomLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
- def __init__(self,parent):
- slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic.__init__(self, parent)
- 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,baseUUID):
- 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(baseUUID,self.labelUUID['study'],
- self.dataUUID[type],date,id)
- f=open(studyFile,"w")
- f.write("{}".format(id))
- f.close()
- return studyId
- def generateSOPInstanceUUID(self,type,baseUUID):
- x=datetime.datetime.now()
- date=x.strftime("%Y%m%d")
- instanceFile=os.path.join(self.basePath,'instanceCount'+date+'.txt')
- try:
- f=open(instanceFile,"r")
- id=int(f.readline())
- id=id+1
- f.close()
- except:
- id=0
- instanceId="{}.{}.{}.{}.{}".format(baseUUID,self.labelUUID['instance'],
- self.dataUUID[type],date,id)
- f=open(instanceFile,"w")
- f.write("{}".format(id))
- f.close()
- return instanceId
- def generateFrameOfReferenceUUID(self,type,baseUUID):
- 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(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,baseUUID):
- x=datetime.datetime.now()
- seriesInstanceUid=baseUUID+'.'+self.labelUUID['series']+'.'
- seriesInstanceUid+=self.dataUUID[type]+'.'+x.strftime("%Y%m%d")+'.'+getTimeCode(x)
- 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=metadata['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',metadata['baseUUID'])
- 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 exportNodeAsDICOM(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'])
- #fdirReg=os.path.join(fdir,'Registration')
- if not os.path.isdir(fdir):
- os.makedirs(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)
- def getTimeCode(x):
- hour=x.strftime("%H")
- hour=re.sub('^0','',hour)
- return hour+x.strftime("%M%S")
-
|