|
@@ -0,0 +1,177 @@
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import numpy
|
|
|
+import random
|
|
|
+import SimpleITK
|
|
|
+import datetime
|
|
|
+
|
|
|
+def initDB(site,returnFB=False):
|
|
|
+ home=os.path.expanduser('~'),
|
|
|
+ nixSuite=os.path.join(home,'software','src','nixSuite')
|
|
|
+ sys.path.append(os.path.join(nixSuite,'wrapper'))
|
|
|
+ import nixWrapper
|
|
|
+ nixWrapper.loadLibrary('labkeyInterface')
|
|
|
+ import labkeyInterface
|
|
|
+ import labkeyDatabaseBrowser
|
|
|
+ import labkeyFileBrowser
|
|
|
+ net=labkeyInterface.labkeyInterface()
|
|
|
+ fconfig=os.path.join(home,'.labkey','{}.json'.format(site))
|
|
|
+ net.init(fconfig)
|
|
|
+ net.getCSRF()
|
|
|
+ if not returnFB:
|
|
|
+ return labkeyDatabaseBrowser.labkeyDB(net)
|
|
|
+ return labkeyDatabaseBrowser.labkeyDB(net),
|
|
|
+ labkeyFileBrowser.labkeyFileBrowser(net)
|
|
|
+
|
|
|
+def initOrthanc(site,returnFB=False):
|
|
|
+
|
|
|
+ home=os.path.expanduser('~'),
|
|
|
+ nixSuite=os.path.join(home,'software','src','nixSuite')
|
|
|
+ sys.path.append(os.path.join(nixSuite,'wrapper'))
|
|
|
+ import nixWrapper
|
|
|
+ nixWrapper.loadLibrary('orthancInterface')
|
|
|
+ import orthancInterface
|
|
|
+ import orthancDatabaseBrowser
|
|
|
+ import orthancFileBrowser
|
|
|
+ net=orthancInterface.orthancInterface()
|
|
|
+ fconfig=os.path.join(home,'.labkey','{}.json'.format(site))
|
|
|
+ net.init(fconfig)
|
|
|
+ if not returnFB:
|
|
|
+ return orthancDatabaseBrowser.orthancDB(net)
|
|
|
+ return orthancDatabaseBrowser.orthancDB(net),
|
|
|
+ orthancFileBrowser.orthancFileBrowser(net)
|
|
|
+
|
|
|
+def parsePos(x):
|
|
|
+ return [float(y) for y in x.split('\\')]
|
|
|
+
|
|
|
+def parseOrientation(x):
|
|
|
+ z=[float(y) for y in x.split('\\')]
|
|
|
+ O=[z[0:3],z[3:6]]
|
|
|
+ O.append(list(numpy.cross(numpy.array(O[0]),numpy.array(O[1]))))
|
|
|
+ return O
|
|
|
+
|
|
|
+def getGeometry(code,odb,seriesId):
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ sd=odb.getData('series',seriesId)
|
|
|
+ firstId=sd['Instances'][0]
|
|
|
+
|
|
|
+ ior=parseOrientation(odb.getDicomField(firstId,'0020-0037'))
|
|
|
+
|
|
|
+ ips=parsePos(odb.getDicomField(firstId,'0028-0030'))
|
|
|
+ ist=parsePos(odb.getDicomField(firstId,'0018-0050'))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ ipos=[parsePos(odb.getDicomField(y,'0020-0032')) for y in sd['Instances']]
|
|
|
+
|
|
|
+ m=[numpy.dot(numpy.array(ior[2]),numpy.array(x)) for x in ipos]
|
|
|
+
|
|
|
+ m1=numpy.sort(m)
|
|
|
+ dm=m1[1:]-m1[:-1]
|
|
|
+ zSpc=numpy.average(dm)
|
|
|
+ zDev=numpy.std(dm)
|
|
|
+ print(f'sliceThickness={zSpc} +- {zDev}')
|
|
|
+ ips.append(zSpc)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ xdir=[y for y in ior]
|
|
|
+ for i in range(3):
|
|
|
+ xdir[i]=[ips[i]*w for w in xdir[i]]
|
|
|
+
|
|
|
+
|
|
|
+ im=numpy.argmin(numpy.array(m))
|
|
|
+ print(im)
|
|
|
+ org=ipos[im]
|
|
|
+ print(org)
|
|
|
+ print(xdir)
|
|
|
+ return {f'{code}origin':org,f'{code}orientation':xdir}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+def getFile(db,odb,project,outdir,n1=0,n2=1):
|
|
|
+ schema='study'
|
|
|
+ query='Imaging1'
|
|
|
+
|
|
|
+ ds=db.selectRows(project,schema,query,[])
|
|
|
+ rows=ds['rows']
|
|
|
+ if n2>0:
|
|
|
+ rows=rows[n1:n2]
|
|
|
+ imgs={'CT':'CT_orthancId','PET':'PETWB_orthancId'}
|
|
|
+ files=[]
|
|
|
+ for r in rows:
|
|
|
+ code=r['pseudoCode']
|
|
|
+ doUpdate=False
|
|
|
+ if not code:
|
|
|
+ code='{:16x}'.format(random.randrange(16**16))
|
|
|
+ r['pseudoCode']=code
|
|
|
+ doUpdate=True
|
|
|
+ codes.append(code)
|
|
|
+ fname=os.path.join(outdir,f'{code}.npz')
|
|
|
+ arr={}
|
|
|
+
|
|
|
+ iSUV={x:getSUVfactor(odb,r[imgs[x]]) for x in imgs}
|
|
|
+ arr.update({x:iSUV[x]*numpy.load(odb.getNumpy('series',r[imgs[x]])) for x in imgs})
|
|
|
+
|
|
|
+ igeo={x:getGeometry(x,odb,r[imgs[x]]) for x in imgs}
|
|
|
+
|
|
|
+
|
|
|
+ _={arr.update(igeo[x]) for x in igeo}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ numpy.savez_compressed(fname,**arr)
|
|
|
+ files.append(fname)
|
|
|
+ if doUpdate:
|
|
|
+ db.modifyRows('update',project,schema,query,[r])
|
|
|
+ return files
|
|
|
+
|
|
|
+def mergeDateAndTime(date,time):
|
|
|
+ time=time.replace(' ','')
|
|
|
+ dateTimeStr=' '.join([date,time])
|
|
|
+
|
|
|
+ dt=datetime.datetime.strptime(dateTimeStr,'%Y%m%d %H%M%S.%f')
|
|
|
+
|
|
|
+ return dt
|
|
|
+
|
|
|
+def getSUVfactor(odb,seriesId):
|
|
|
+ sd=odb.getData('series',seriesId)
|
|
|
+ orthancId=sd['Instances'][0]
|
|
|
+
|
|
|
+ MODALITY='0008-0060'
|
|
|
+ PATIENT_WEIGHT='0010-1030'
|
|
|
+ RADIOPHARMACEUTICAL_STARTTIME='0054-0016/0/0018-1072'
|
|
|
+ RADIONUCLIDE_TOTALDOSE='0054-0016/0/0018-1074'
|
|
|
+ RADIONUCLIDE_HALFLIFE='0054-0016/0/0018-1075'
|
|
|
+ RADIOPHARMACEUTICAL_STARTDATETIME='0054-0016/0/0018-1078'
|
|
|
+ SERIES_TIME='0008-0031'
|
|
|
+ SERIES_DATE='0008-0021'
|
|
|
+ modality=odb.getDicomField(orthancId,MODALITY)
|
|
|
+ if modality!='PT':
|
|
|
+ print('Returning 1')
|
|
|
+ return 1
|
|
|
+ weight = float(odb.getDicomField(orthancId,PATIENT_WEIGHT))
|
|
|
+
|
|
|
+ halfLife = float(odb.getDicomField(orthancId,RADIONUCLIDE_HALFLIFE))
|
|
|
+
|
|
|
+ injDose = float(odb.getDicomField(orthancId,RADIONUCLIDE_TOTALDOSE))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ injDateTime = mergeDateAndTime(odb.getDicomField(orthancId,SERIES_DATE),
|
|
|
+ odb.getDicomField(orthancId,RADIOPHARMACEUTICAL_STARTTIME))
|
|
|
+ scanDateTime=mergeDateAndTime(odb.getDicomField(orthancId,SERIES_DATE),
|
|
|
+ odb.getDicomField(orthancId,SERIES_TIME))
|
|
|
+ t=(scanDateTime-injDateTime).total_seconds()
|
|
|
+ scanDose = injDose*numpy.exp(-(numpy.log(2)*t)/halfLife);
|
|
|
+ SUV_factor = 1/((scanDose/weight)*0.001);
|
|
|
+ print(SUV_factor)
|
|
|
+ return SUV_factor
|
|
|
+
|
|
|
+
|