123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972 |
- import os
- import unittest
- from __main__ import vtk, qt, ctk, slicer
- from slicer.ScriptedLoadableModule import *
- import json
- import datetime
- import sys
- import nixModule
- import pathlib
- import chardet
- import re
- import functools
- #
- # labkeySlicerPythonExtension
- #
- class segmentationBrowser(ScriptedLoadableModule):
- """Uses ScriptedLoadableModule base class, available at:
- https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
- """
- def __init__(self, parent):
- ScriptedLoadableModule.__init__(self, parent)
- self.parent.title = "segmentation Browser"
- # TODO make this more human readable by adding spaces
- self.parent.categories = ["LabKey"]
- self.parent.dependencies = []
- self.parent.contributors = ["Andrej Studen (UL/FMF)"]
- # replace with "Firstname Lastname (Organization)"
- self.parent.helpText = """
- Interface to stored segmentation files in LabKey
- """
- self.parent.acknowledgementText = """
- Developed within the medical physics research programme
- of the Slovenian research agency.
- """ # replace with organization, grant and thanks.
- #
- # labkeySlicerPythonExtensionWidget
- #
- class segmentationBrowserWidget(ScriptedLoadableModuleWidget):
- """Uses ScriptedLoadableModuleWidget base class, available at:
- https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
- """
- def setup(self):
- print("Setting up segmentationBrowserWidget")
- ScriptedLoadableModuleWidget.setup(self)
- # Instantiate and connect widgets ...
-
- self.logic=segmentationBrowserLogic(self)
- self.addInfoSection()
- self.addSetupSection()
- self.addPatientsSelector()
- self.addSegmentViewer()
- self.addWindowManipulator()
- def addInfoSection(self):
- #a python overview of json settings
- infoCollapsibleButton = ctk.ctkCollapsibleButton()
- infoCollapsibleButton.text = "Info"
- self.layout.addWidget(infoCollapsibleButton)
-
- infoLayout = qt.QFormLayout(infoCollapsibleButton)
- self.participantField=qt.QLabel("PatientId")
- infoLayout.addRow("Participant field:",self.participantField)
-
- self.ctField=qt.QLabel("ctResampled")
- infoLayout.addRow("Data field (CT):",self.ctField)
- self.petField=qt.QLabel("petResampled")
- infoLayout.addRow("Data field (PET):",self.petField)
- self.userField=qt.QLabel("Loading")
- infoLayout.addRow("User",self.userField)
- self.idField=qt.QLabel("Loading")
- infoLayout.addRow("ID",self.idField)
- #Add logic at some point
- #self.logic=segmentationBrowserLogic(self)
- def addPatientsSelector(self):
- #
- # Patients Area
- #
- patientsCollapsibleButton = ctk.ctkCollapsibleButton()
- patientsCollapsibleButton.text = "Patients"
-
- #don't add it yet
- self.layout.addWidget(patientsCollapsibleButton)
- patientsFormLayout = qt.QFormLayout(patientsCollapsibleButton)
- self.patientList=qt.QComboBox()
- self.patientList.currentIndexChanged.connect(self.onPatientListChanged)
- self.patientList.setEditable(True)
- self.patientList.setInsertPolicy(qt.QComboBox.NoInsert)
- patientsFormLayout.addRow("Patient:",self.patientList)
-
- self.visitList=qt.QComboBox()
- self.visitList.currentIndexChanged.connect(self.onVisitListChanged)
- patientsFormLayout.addRow("Visit:",self.visitList)
- self.ctCode=qt.QLabel("ctCode")
- patientsFormLayout.addRow("CT:",self.ctCode)
-
- self.petCode=qt.QLabel("petCode")
- patientsFormLayout.addRow("PET:",self.petCode)
- self.patientLoad=qt.QPushButton("Load")
- self.patientLoad.clicked.connect(self.onPatientLoadButtonClicked)
- patientsFormLayout.addRow("Load patient",self.patientLoad)
- #no Save button
- #self.patientSave=qt.QPushButton("Save")
- #self.patientSave.clicked.connect(self.onPatientSaveButtonClicked)
- #patientsFormLayout.addRow("Save segmentation",self.patientSave)
- self.patientClear=qt.QPushButton("Clear")
- self.patientClear.clicked.connect(self.onPatientClearButtonClicked)
- patientsFormLayout.addRow("Clear patient",self.patientClear)
-
- self.keepCached=qt.QCheckBox("keep Cached")
- self.keepCached.setChecked(1)
- patientsFormLayout.addRow("Keep cached",self.keepCached)
-
- def addSetupSection(self):
- setupCollapsibleButton = ctk.ctkCollapsibleButton()
- setupCollapsibleButton.text = "Setup"
- self.layout.addWidget(setupCollapsibleButton)
- #Form layout (maybe one can think of more intuitive layouts)
- setupFormLayout = qt.QFormLayout(setupCollapsibleButton)
- self.serverList=qt.QComboBox()
- self.serverList.addItem('<Select>')
- self.serverList.addItem("astuden")
- self.serverList.addItem("adoma")
- self.serverList.addItem("kzevnik")
- self.serverList.currentIndexChanged.connect(self.onServerListChanged)
- setupFormLayout.addRow("Select user:",self.serverList)
- self.setupList=qt.QComboBox()
- self.setupList.addItem('<Select>')
- self.setupList.addItem("limfomiPET_iBrowser.json")
- self.setupList.addItem("iraemm_iBrowserProspective.json")
- self.setupList.addItem("iraemm_iBrowserRetrospective.json")
- self.setupList.addItem("ORLPET_iBrowser.json")
- self.setupList.currentIndexChanged.connect(self.onSetupListChanged)
- setupFormLayout.addRow("Setup:",self.setupList)
- def addReviewSection(self):
- #
- # Review Area (not used, relic)
- #
- reviewCollapsibleButton = ctk.ctkCollapsibleButton()
- reviewCollapsibleButton.text = "Review"
- self.layout.addWidget(reviewCollapsibleButton)
-
- self.reviewBoxLayout = qt.QVBoxLayout(reviewCollapsibleButton)
- self.reviewFormLayout = qt.QFormLayout()
- self.reviewSegment=qt.QComboBox()
- self.reviewSegment.currentIndexChanged.connect(\
- self.onReviewSegmentChanged)
- self.reviewFormLayout.addRow("Selected region:",self.reviewSegment)
-
-
- self.reviewResult=qt.QComboBox()
- sLabel="What do you think about the segmentation:"
- self.reviewFormLayout.addRow(sLabel,self.reviewResult)
- reviewOptions=['Select','Excellent','Minor deficiencies',\
- 'Major deficiencies','Unusable']
- for opt in reviewOptions:
- self.reviewResult.addItem(opt)
-
- self.aeResult=qt.QComboBox()
- aeLabel="Is organ suffering from adverse effect?"
- self.reviewFormLayout.addRow(aeLabel,self.aeResult)
- aeOptions=['Select','Yes','No']
- for opt in aeOptions:
- self.aeResult.addItem(opt)
- #self.aeResult.setCurrentIndex(0)
- self.updateReview=qt.QPushButton("Save")
- saLabel="Save segmentation and AE decision for current segment"
- self.reviewFormLayout.addRow(saLabel,self.updateReview)
- self.updateReview.clicked.connect(self.onUpdateReviewButtonClicked)
- self.reviewBoxLayout.addLayout(self.reviewFormLayout)
- submitFrame=qt.QGroupBox("Submit data")
-
- self.submitFormLayout=qt.QFormLayout()
- self.reviewComment=qt.QTextEdit("this is a test")
- self.submitFormLayout.addRow("Comments (optional)",self.reviewComment)
-
- self.submitReviewButton=qt.QPushButton("Submit")
- self.submitFormLayout.addRow("Submit to database",\
- self.submitReviewButton)
- self.submitReviewButton.clicked.connect(\
- self.onSubmitReviewButtonClicked)
-
- submitFrame.setLayout(self.submitFormLayout)
- submitFrame.setFlat(1)
- #submitFrame.setFrameShape(qt.QFrame.StyledPanel)
- #submitFrame.setFrameShadow(qt.QFrame.Sunken)
- submitFrame.setStyleSheet("background-color:rgba(220,215,180,45)")
- self.reviewBoxLayout.addWidget(submitFrame)
- def addSegmentViewer(self):
- #not used (for review)
- editorCollapsibleButton = ctk.ctkCollapsibleButton()
- editorCollapsibleButton.text = "Segment Viewer"
- self.layout.addWidget(editorCollapsibleButton)
- self.vSegViewLayout=qt.QVBoxLayout(editorCollapsibleButton)
- segList=' '.join(self.logic.segmentList)
- label=qt.QLabel(" {}".format(segList))
- self.vSegViewLayout.addWidget(label)
- self.segList=[]
-
- def addWindowManipulator(self):
- windowManipulatorCollapsibleButton=ctk.ctkCollapsibleButton()
- windowManipulatorCollapsibleButton.text="CT Window Manipulator"
- self.layout.addWidget(windowManipulatorCollapsibleButton)
- hLayout=qt.QHBoxLayout(windowManipulatorCollapsibleButton)
- ctWins={'CT:bone':self.onCtBoneButtonClicked,
- 'CT:air':self.onCtAirButtonClicked,
- 'CT:abdomen':self.onCtAbdomenButtonClicked,
- 'CT:brain':self.onCtBrainButtonClicked,
- 'CT:lung':self.onCtLungButtonClicked}
- for ctWin in ctWins:
- ctButton=qt.QPushButton(ctWin)
- ctButton.clicked.connect(ctWins[ctWin])
- hLayout.addWidget(ctButton)
- def onSetupListChanged(self,i):
- status=self.logic.setConfig(self.setupList.currentText)
- try:
- if status['error']=='FILE NOT FOUND':
- print('File {} not found.'.format(self.setupList.currentText))
- return
- except KeyError:
- pass
- #sort ids
- ids=status['ids']
- ids.sort()
- self.updatePatientList(ids)
- self.onPatientListChanged(0)
-
- def onServerListChanged(self,i):
- status=self.logic.setServer(self.serverList.currentText)
- try:
- if status['error']=='KEY ERROR':
- self.serverList.setStyleSheet('background-color: violet')
- if status['error']=='ID ERROR':
- self.serverList.setStyleSheet('background-color: red')
- return
- except KeyError:
- pass
- self.idField.setText(status['id'])
- self.userField.setText(status['displayName'])
- self.serverList.setStyleSheet('background-color: green')
- def onPatientListChanged(self,i):
- self.visitList.clear()
- self.petCode.setText("")
- self.ctCode.setText("")
-
- #add potential filters from setup to dbFilter
- ds=self.logic.getDataset(dbFilter={'participant':self.patientList.currentText})
-
- visitVar=self.logic.getVarName(var='visitField')
- dt=datetime.datetime
-
- #label is a combination of sequence number and date of imaging
- try:
- seq={row['SequenceNum']:
- {'label':row[visitVar],
- 'date': dt.strptime(row['studyDate'],'%Y/%m/%d %H:%M:%S')}
- for row in ds['rows']}
- except TypeError:
- #if studyDate is empty, this will return no possible visits
- return
- #apply lookup to visitVar if available
- try:
- seq={x:
- {'label':ds['lookups'][visitVar][seq[x]['label']],
- 'date':seq[x]['date']}
- for x in seq}
- except KeyError:
- pass
- #format label
- seq={x:'{} ({})'.format(seq[x]['label'],dt.strftime(seq[x]['date'],'%d-%b-%Y'))
- for x in seq}
-
- for s in seq:
- #onVisitListChanged is called for every addItem
- self.visitList.addItem(seq[s],s)
- #self.onVisitListChanged(0)
- def onVisitListChanged(self,i):
-
- #ignore calls on empty list
- if self.visitList.count==0:
- return
- #get sequence num
- s=self.visitList.itemData(i)
- print("Visit: SequenceNum:{}, label{}".format(s,self.visitList.currentText))
- dbFilter={'participant':self.patientList.currentText,
- 'seqNum':s}
- ds=self.logic.getDataset(dbFilter=dbFilter)
- if not len(ds['rows'])==1:
- print("Found incorrect number {} of matches for [{}]/[{}]".\
- format(len(ds['rows']),\
- self.patientList.currentText,s))
- row=ds['rows'][0]
- #copy row properties for data access
- self.currentRow=row
- self.petCode.setText(row[self.petField.text])
- self.ctCode.setText(row[self.ctField.text])
- #self.segmentationCode.setText(row[self.segmentationField.text])
- def updatePatientList(self,ids):
- self.patientList.clear()
- for id in ids:
- self.patientList.addItem(id)
- def onPatientLoadButtonClicked(self):
- #clear first
- self.logic.clearVolumesAndSegmentations()
- print("Load")
- #delegate loading to logic
- self.logic.loadImages(self.currentRow,self.keepCached.isChecked())
- self.logic.loadSegmentations(self.currentRow)
- self.updateSegmentationViewer(self.logic.segmentationRows)
- #self.logic.loadReview(self.currentRow)
- #self.logic.loadAE(self.currentRow)
- #self.onReviewSegmentChanged()
- def generateSegmentationView(self):
- s=qt.QHBoxLayout()
- nm=qt.QLabel("name")
- s.addWidget(nm)
- for g in self.logic.segmentList:
- t=qt.QCheckBox()
- s.addWidget(t)
- return s
-
- def setSegmentationViewName(self,s,name):
- label=s.itemAt(0).widget()
- label.setText(name)
- def getWidgetsFromLayout(self,s):
- return [s.itemAt(i).widget() for i in range(s.count())]
-
- def hideLayout(self,s):
- for w in self.getWidgetsFromLayout(s):
- w.hide()
-
- def disconnect(self,s):
- for w in self.getWidgetsFromLayout(s)[1:]:
- try:
- w.stateChanged.disconnect()
- except TypeError:
- continue
-
-
- def showLayout(self,s):
- for w in self.getWidgetsFromLayout(s):
- w.show()
-
- def refreshCallbacks(self,s,entry):
- w=self.getWidgetsFromLayout(s)
- for i in range(len(self.logic.segmentList)):
- w[i+1].stateChanged.connect(functools.partial(self.onSegmentationViewChanged,
- latestFile=entry['latestFile'],
- segment=self.logic.segmentList[i]))
-
- def getUserMap(self):
- users=self.logic.lookups['core:Users']
- #print('{}'.format(users))
- return users
- def updateSegmentationViewer(self,rows):
- users=self.getUserMap()
- for s in self.segList:
- self.hideLayout(s)
- self.disconnect(s)
- #can't hide a layout
-
- #s.hide()
- i=0
- for r in rows:
- try:
- s=self.segList[i]
- except IndexError:
- #assume we only have to add one (since we iterate sequentially)
- self.segList.append(self.generateSegmentationView())
- s=self.segList[i]
- self.vSegViewLayout.addLayout(s)
- self.showLayout(s)
- self.setSegmentationViewName(s,users[r['User']])
- self.refreshCallbacks(s,r)
- i+=1
-
- def onReviewSegmentChanged(self):
- pass
- def onPatientClearButtonClicked(self):
- self.logic.clearVolumesAndSegmentations()
- def onPatientSaveButtonClicked(self):
- #not used
- self.logic.saveSegmentation()
- def onCtBoneButtonClicked(self):
- self.logic.setWindow('CT:bone')
- def onCtAirButtonClicked(self):
- self.logic.setWindow('CT:air')
- def onCtAbdomenButtonClicked(self):
- self.logic.setWindow('CT:abdomen')
- def onCtBrainButtonClicked(self):
- self.logic.setWindow('CT:brain')
- def onCtLungButtonClicked(self):
- self.logic.setWindow('CT:lung')
- def onSegmentationViewChanged(self,state,latestFile,segment):
- print('onSegmentationViewChanged {} {} {} '.format(latestFile,segment,state))
- self.logic.toggleSegmentVisibility(latestFile,segment,state>0)
- def cleanup(self):
- pass
- def loadLibrary(name):
- #utility function to load nix library from git
- fwrapper=nixModule.getWrapper('nixWrapper.py')
- p=pathlib.Path(fwrapper)
- sys.path.append(str(p.parent))
- import nixWrapper
- return nixWrapper.loadLibrary(name)
- #
- # segmentationBrowserLogic
- #
- class segmentationBrowserLogic(ScriptedLoadableModuleLogic):
- """This class should implement all the actual
- computation done by your module. The interface
- should be such that other python code can import
- this class and make use of the functionality without
- requiring an instance of the Widget.
- Uses ScriptedLoadableModuleLogic base class, available at:
- https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
- """
- def __init__(self,parent=None):
- ScriptedLoadableModuleLogic.__init__(self, parent)
- print('segmentationBrowserLogic loading')
- if not parent==None:
- #use layout and data from parent widget
- self.parent=parent
-
- fhome=os.path.expanduser('~')
- fsetup=os.path.join(fhome,'.labkey','setup.json')
- try:
- with open(fsetup) as f:
- self.setup=json.load(f)
- except FileNotFoundError:
- self.setup={}
-
- try:
- pt=self.setup['paths']
- except KeyError:
- self.setup['paths']={}
- lName='labkeyInterface'
- loadLibrary(lName)
-
- import labkeyInterface
- import labkeyDatabaseBrowser
- import labkeyFileBrowser
- self.network=labkeyInterface.labkeyInterface()
- self.dbBrowser=labkeyDatabaseBrowser
- self.fBrowser=labkeyFileBrowser
- self.tempDir=os.path.join(os.path.expanduser('~'),'temp')
- if not os.path.isdir(self.tempDir):
- os.mkdir(self.tempDir)
- self.lookups={}
- self.segmentList=["Liver","Blood","Marrow","Disease","Deauville"]
- print('segmentationBrowserLogic setup complete')
-
- def setServer(self,serverName):
- #additional way of setting the labkey network interface
- #if no parent was provided in logic initialization (stand-alone mode)
- status={}
- fileName="NONE"
- if serverName=="astuden":
- fileName="astuden.json"
- if serverName=="adoma":
- fileName="adoma.json"
- if serverName=="kzevnik":
- fileName="kzevnik.json"
- if fileName=="NONE":
- print("No path was associated with server {}".format(serverName))
- status['error']='KEY ERROR'
- return status
- fconfig=os.path.join(os.path.expanduser('~'),'.labkey',fileName)
- self.network.init(fconfig)
- self.remoteId=self.network.getUserId()
- if self.remoteId==None:
- status['error']='ID ERROR'
- return status
-
- status['displayName']=self.remoteId['displayName']
- status['id']=self.remoteId['id']
- #reset db and fb (they are thin classes anyhow)
- self.db=self.dbBrowser.labkeyDB(self.network)
- self.fb=self.fBrowser.labkeyFileBrowser(self.network)
- return status
- def setConfig(self,configName):
- status={}
- fileName=os.path.join(os.path.expanduser('~'),'.labkey',configName)
- if not os.path.isfile(fileName):
- status['error']='FILE NOT FOUND'
- return status
- with open(fileName,'r') as f:
- self.isetup=json.load(f)
- #self.project=self.isetup['project']
- #"iPNUMMretro/Study"
- #self.schema='study'
- #self.dataset=self.isetup['query']
-
- #include filters...
- ds=self.getDataset()
- ids=[row[self.isetup['participantField']] for row in ds['rows']]
- status['ids']=list(set(ids))
- return status
- def getVarName(self,name="Imaging",var="visitField"):
- dset=self.isetup['datasets'][name]
- defaults={"visitField":"imagingVisitId"}
- try:
- return dset[var]
- except KeyError:
- return defaults[var]
-
- def getDataset(self,name="Imaging",dbFilter={}):
- dset=self.isetup['datasets'][name]
- project=dset['project']
- schema=dset['schema']
- query=dset['query']
- #add default filters
- qFilter=[]
- try:
- for qf in dset['filter']:
- v=dset['filter'][qf]
- qFilter.append({'variable':qf,'value':v,'oper':'eq'})
- except KeyError:
- pass
- for f in dbFilter:
- if f=='participant':
- qFilter.append({'variable':self.isetup['participantField'],
- 'value':dbFilter[f],'oper':'eq'})
- continue
- if f=='seqNum':
- qFilter.append({'variable':'SequenceNum',
- 'value':'{}'.format(dbFilter[f]),
- 'oper':'eq'})
- continue
- qFilter.append({'variable':f,'value':dbFilter[f],'oper':'eq'})
- try:
- ds=self.db.selectRows(project,schema,query, \
- qFilter,dset['view'])
- except KeyError:
- ds=self.db.selectRows(project,schema,query,qFilter)
- #get lookups as well
- lookups={}
- for f in ds['metaData']['fields']:
- try:
- lookup=f['lookup']
- except KeyError:
- continue
- var=f['name']
- lookupCode='{}:{}'.format(lookup['schema'],lookup['queryName'])
- try:
- lookups[var]=self.lookups[lookupCode]
- except KeyError:
- self.lookups[lookupCode]=self.loadLookup(project,lookup)
- lookups[var]=self.lookups[lookupCode]
-
- return {'rows':ds['rows'],'lookups':lookups}
- def loadLookup(self,project,lookup):
- qData={}
- key=lookup['keyColumn']
- value=lookup['displayColumn']
- fSet=self.db.selectRows(project,lookup['schema'],lookup['queryName'],[])
- for q in fSet['rows']:
- qData[q[key]]=q[value]
- return qData
- def loadImage(self,iData):
- #unpack iData
- idx=iData['idx']
- path=iData['path']
- keepCached=iData['keepCached']
- dset=self.isetup['datasets'][iData['dataset']]
-
- localPath=os.path.join(self.tempDir,path[-1])
- if not os.path.isfile(localPath):
- #download from server
- remotePath=self.fb.formatPathURL(dset['project'],'/'.join(path))
- if not self.fb.entryExists(remotePath):
- print("Failed to get {}".format(remotePath))
- return
- self.fb.readFileToFile(remotePath,localPath)
-
- properties={}
- filetype='VolumeFile'
- #make sure segmentation gets loaded as a labelmap
- if idx=="Segmentation":
- filetype='SegmentationFile'
- #properties["labelmap"]=1
- try:
- volumeIdx=iData['volumeIdx']
- except KeyError:
- volumeIdx=idx
-
- self.volumeNode[volumeIdx]=slicer.util.loadNodeFromFile(localPath,
- filetype=filetype,properties=properties)
-
- if not keepCached:
- os.remove(localPath)
- def loadImages(self,row,keepCached):
-
-
- #fields={'ctResampled':True,'petResampled':False}
- fields={"CT":self.parent.ctField.text,\
- "PET":self.parent.petField.text}
- path=[self.isetup['imageDir'],row['patientCode'],row['visitCode']]
- relativePaths={x:path+[row[y]] for (x,y) in fields.items()}
- self.volumeNode={}
- for f in relativePaths:
- iData={'idx':f,'path':relativePaths[f],
- 'keepCached':keepCached,'dataset':'Imaging'}
- self.loadImage(iData)
- #mimic abdominalCT standardized window setting
- self.volumeNode['CT'].GetScalarVolumeDisplayNode().\
- SetAutoWindowLevel(False)
- self.volumeNode['CT'].GetScalarVolumeDisplayNode().\
- SetWindowLevel(1400, -500)
- #set colormap for PET to PET-Heat (this is a verbatim setting from
- #the Volumes->Display->Lookup Table colormap identifier)
- self.volumeNode['PET'].GetScalarVolumeDisplayNode().\
- SetAndObserveColorNodeID(\
- slicer.util.getNode('Inferno').GetID())
- slicer.util.setSliceViewerLayers(background=self.volumeNode['CT'],\
- foreground=self.volumeNode['PET'],foregroundOpacity=0.5,fit=True)
- def loadSegmentations(self,row, loadFile=1):
- dbFilter={
- #'User':'{}'.format(self.remoteId['id']),
- 'participant':row[self.isetup['participantField']],
- 'visitCode':row['visitCode']}
- ds=self.getDataset(name='SegmentationsMaster',
- dbFilter=dbFilter)
- self.segmentationRows=ds['rows']
- if len(self.segmentationRows)<0:
- print('No segmentations for {}'.format(dbFilter))
- return
- for r in self.segmentationRows:
- #update self.segmentationEntry
- #self.segmentationEntry=ds['rows'][0]
- #self.segmentationEntry['origin']='database'
- #if loadFile:
- self.loadSegmentationFromEntry(r)
- #make invisile by default
- for s in self.segmentList:
- self.toggleSegmentVisibility(r['latestFile'],s,False)
- print('load Segmentation: done')
- #create new segmentation
- #self.createSegmentation(row)
- def getSegmentationPath(self,entry):
- path=[self.isetup['imageDir'],
- entry['patientCode'],
- entry['visitCode']]
- path.append('Segmentations')
- return path
- def loadSegmentationFromEntry(self,r):
- #compile path
- entry=r
- path=self.getSegmentationPath(entry)
- path.append(entry['latestFile'])
- iData={'idx':'Segmentation','path':path,
- 'keepCached':0,'dataset':'SegmentationsMaster',
- 'volumeIdx':entry['latestFile']}
- self.loadImage(iData)
- #look for missing segments
- return
- #skip this
- segNode=self.volumeNode['Segmentation']
- seg=segNode.GetSegmentation()
- segNames=[seg.GetNthSegmentID(i) for i in range(seg.GetNumberOfSegments())]
- print('Segments')
- try:
- segmentList=self.isetup['segmentList']
- except KeyError:
- segmentList=self.segmentList
- for s in segmentList:
- if s not in segNames:
- seg.AddEmptySegment(s,s)
- print(s)
- print('Done')
- def toggleSegmentVisibility(self,latestFile,segmentId,visible):
- segNode=self.volumeNode[latestFile]
- if segNode==None:
- print('Could not find segNode')
- return
- seg=segNode.GetSegmentation()
- segment=seg.GetSegment(segmentId)
- if segment==None:
- print('Could not find segment {}'.format(segmentId))
- return
- segNode.GetDisplayNode().SetSegmentVisibility(segmentId, visible)
- def saveSegmentation(self):
- #not used
- #get the latest key by adding an entry to SegmentationList
- copyFields=[self.isetup['participantField'],'patientCode','visitCode','User']
- outRow={x:self.segmentationEntry[x] for x in copyFields}
- sList=self.isetup['datasets']['SegmentationsList']
- resp=self.db.modifyRows('insert',sList['project'],
- sList['schema'],sList['query'],[outRow])
- encoding=chardet.detect(resp)['encoding']
- respJSON=json.loads(resp.decode(encoding))
- outRow=respJSON['rows'][0]
- #print(outRow)
- #construct file name with known key
- uName=re.sub(' ','_',self.remoteId['displayName'])
- fName='Segmentation_{}-{}_{}_{}.nrrd'.format(
- self.segmentationEntry['patientCode'],
- self.segmentationEntry['visitCode'],
- uName,outRow['Key'])
- path=self.getSegmentationPath()
- path.append(fName)
- self.saveNode(self.volumeNode['Segmentation'],'SegmentationsMaster',path)
-
- #update SegmentationList with know file name
- outRow['Segmentation']=fName
- self.db.modifyRows('update',sList['project'],
- sList['schema'],sList['query'],[outRow])
- #update SegmentationsMaster
- self.segmentationEntry['latestFile']=fName
- self.segmentationEntry['version']=outRow['Key']
- des=self.isetup['datasets']['SegmentationsMaster']
- op='insert'
- if self.segmentationEntry['origin']=='database':
- op='update'
- print('Saving: mode={}'.format(op))
- resp=self.db.modifyRows(op,des['project'],
- des['schema'],des['query'],[self.segmentationEntry])
- print(resp)
- #since we loaded a version, origin should be set to database
- self.loadSegmentation(self.segmentationEntry,0)
- #self.segmentationEntry['origin']='database'
-
- def saveNode(self,node,dataset,path):
- #not used
- fName=path[-1]
- localPath=os.path.join(self.tempDir,fName)
- slicer.util.saveNode(node,localPath)
- dset=self.isetup['datasets'][dataset]
- #exclude file name when building directory structure
- remotePath=self.fb.buildPathURL(dset['project'],path[:-1])
- remotePath+='/'+fName
- self.fb.writeFileToFile(localPath,remotePath)
- #add entry to segmentation list
- def createSegmentation(self,entry):
- #not used
- #create segmentation entry for database update
- #note that origin is not set to database
- copyFields=[self.isetup['participantField'],'patientCode','visitCode','SequenceNum']
- #copyFields=['ParticipantId','patientCode','visitCode','SequenceNum']
- self.segmentationEntry={x:entry[x] for x in copyFields}
- self.segmentationEntry['User']=self.remoteId['id']
- self.segmentationEntry['origin']='created'
- self.segmentationEntry['version']=-1111
-
- #create a segmentation node
- uName=re.sub(' ','_',self.remoteId['displayName'])
- segNode=slicer.vtkMRMLSegmentationNode()
- self.volumeNode['Segmentation']=segNode
- segNode.SetName('Segmentation_{}_{}_{}'.
- format(entry['patientCode'],entry['visitCode'],uName))
- slicer.mrmlScene.AddNode(segNode)
- segNode.CreateDefaultDisplayNodes()
- segNode.SetReferenceImageGeometryParameterFromVolumeNode(self.volumeNode['PET'])
- try:
- segmentList=self.isetup['segmentList']
- except KeyError:
- segmentList=self.segmentList
- for s in segmentList:
- segNode.GetSegmentation().AddEmptySegment(s,s)
- def clearVolumesAndSegmentations(self):
- nodes=slicer.util.getNodesByClass("vtkMRMLVolumeNode")
- nodes.extend(slicer.util.getNodesByClass("vtkMRMLSegmentationNode"))
- res=[slicer.mrmlScene.RemoveNode(f) for f in nodes]
- try:
- self.volumeNode.clear()
- except AttributeError:
- pass
- #self.segmentationNode=None
- #self.reviewResult={}
- #self.aeList={}
- def setWindow(self,windowName):
- #default
- w=1400
- l=-500
-
- if windowName=='CT:bone':
- w=1000
- l=400
- if windowName=='CT:air':
- w=1000
- l=-426
-
- if windowName=='CT:abdomen':
- w=350
- l=40
- if windowName=='CT:brain':
- w=100
- l=50
- if windowName=='CT:lung':
- w=1400
- l=-500
- self.volumeNode['CT'].GetScalarVolumeDisplayNode().\
- SetWindowLevel(w,l)
-
- print('setWindow: {} {}/{}'.format(windowName,w,l))
- class segmentationBrowserTest(ScriptedLoadableModuleTest):
- """
- This is the test case for your scripted module.
- Uses ScriptedLoadableModuleTest base class, available at:
- https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
- """
- def setup(self):
- """ Do whatever is needed to reset the state - typically a scene clear will be enough.
- """
- slicer.mrmlScene.Clear(0)
- def runTest(self):
- """Run as few or as many tests as needed here.
- """
- self.setUp()
- self.test_irAEMMBrowser()
- def test_irAEMMBrowser(self):
- """ Ideally you should have several levels of tests. At the lowest level
- tests sould exercise the functionality of the logic with different inputs
- (both valid and invalid). At higher levels your tests should emulate the
- way the user would interact with your code and confirm that it still works
- the way you intended.
- One of the most important features of the tests is that it should alert other
- developers when their changes will have an impact on the behavior of your
- module. For example, if a developer removes a feature that you depend on,
- your test should break so they know that the feature is needed.
- """
- self.delayDisplay("Starting the test")
- #
- # first, get some data
- #
|