{ "cells": [ { "cell_type": "code", "execution_count": 11, "id": "1da1e1c8-1f5a-4c9e-aa4a-1baaa8c71df1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "loadLibrary\n", "remoteSourcesURL https://git0.fmf.uni-lj.si/studen/nixSuite/raw/master/remoteResources/resources.json\n", "{'labkeyInterface': {'url': 'https://git0.fmf.uni-lj.si/studen/labkeyInterface/archive/master.zip', 'branch': 'master', 'modules': []}, 'irAEMM': {'url': 'https://git0.fmf.uni-lj.si/studen/iraemm/archive/master.zip', 'branch': 'master', 'modules': ['iraemmBrowser']}, 'SlicerLabkeyExtension': {'url': 'https://git0.fmf.uni-lj.si/studen/SlicerLabkeyExtension/archive/SlicerExtensionIndex.zip', 'branch': 'SlicerExtensionIndex', 'modules': ['labkeyBrowser']}, 'limfomiPET': {'url': 'https://git0.fmf.uni-lj.si/studen/limfomiPET/archive/master.zip', 'branch': 'master', 'modules': ['imageBrowser', 'segmentationBrowser']}, 'parseConfig': {'url': 'https://git0.fmf.uni-lj.si/studen/parseConfig/archive/master.zip', 'branch': 'master', 'modules': []}, 'orthancInterface': {'url': 'https://git0.fmf.uni-lj.si/studen/orthancInterface/archive/master.zip', 'branch': 'master', 'modules': []}, 'dynamicSPECT': {'url': 'https://git0.fmf.uni-lj.si/studen/dynamicSPECT/archive/master.zip', 'branch': 'master', 'modules': ['imageBrowser']}}\n", "{'url': 'https://git0.fmf.uni-lj.si/studen/labkeyInterface/archive/master.zip', 'branch': 'master', 'modules': []}\n", "File C:\\Users\\studen\\temp\\labkeyInterface.zip: True\n", "loadLibrary\n", "remoteSourcesURL https://git0.fmf.uni-lj.si/studen/nixSuite/raw/master/remoteResources/resources.json\n", "{'labkeyInterface': {'url': 'https://git0.fmf.uni-lj.si/studen/labkeyInterface/archive/master.zip', 'branch': 'master', 'modules': []}, 'irAEMM': {'url': 'https://git0.fmf.uni-lj.si/studen/iraemm/archive/master.zip', 'branch': 'master', 'modules': ['iraemmBrowser']}, 'SlicerLabkeyExtension': {'url': 'https://git0.fmf.uni-lj.si/studen/SlicerLabkeyExtension/archive/SlicerExtensionIndex.zip', 'branch': 'SlicerExtensionIndex', 'modules': ['labkeyBrowser']}, 'limfomiPET': {'url': 'https://git0.fmf.uni-lj.si/studen/limfomiPET/archive/master.zip', 'branch': 'master', 'modules': ['imageBrowser', 'segmentationBrowser']}, 'parseConfig': {'url': 'https://git0.fmf.uni-lj.si/studen/parseConfig/archive/master.zip', 'branch': 'master', 'modules': []}, 'orthancInterface': {'url': 'https://git0.fmf.uni-lj.si/studen/orthancInterface/archive/master.zip', 'branch': 'master', 'modules': []}, 'dynamicSPECT': {'url': 'https://git0.fmf.uni-lj.si/studen/dynamicSPECT/archive/master.zip', 'branch': 'master', 'modules': ['imageBrowser']}}\n", "{'url': 'https://git0.fmf.uni-lj.si/studen/orthancInterface/archive/master.zip', 'branch': 'master', 'modules': []}\n", "File C:\\Users\\studen\\temp\\orthancInterface.zip: True\n" ] } ], "source": [ "import sys\n", "import os\n", "import importlib\n", "import numpy\n", "import random\n", "import SimpleITK\n", "import datetime\n", "\n", "nixSuite=os.path.join(os.path.expanduser('~'),'software','src','nixSuite')\n", "sys.path.append(os.path.join(nixSuite,'wrapper'))\n", "import nixWrapper\n", "nixWrapper.loadLibrary('labkeyInterface')\n", "import labkeyInterface\n", "import labkeyDatabaseBrowser\n", "import labkeyFileBrowser\n", "\n", "\n", "nixWrapper.loadLibrary('orthancInterface')\n", "import orthancInterface\n", "import orthancDatabaseBrowser\n", "import orthancFileBrowser" ] }, { "cell_type": "code", "execution_count": 2, "id": "40fce609-1142-4746-a912-70481ff809be", "metadata": {}, "outputs": [], "source": [ "def initDB(site,returnFB=False):\n", " net=labkeyInterface.labkeyInterface()\n", " fconfig=os.path.join(os.path.expanduser('~'),'.labkey','{}.json'.format(site))\n", " net.init(fconfig)\n", " net.getCSRF()\n", " if not returnFB:\n", " return labkeyDatabaseBrowser.labkeyDB(net)\n", " return labkeyDatabaseBrowser.labkeyDB(net),labkeyFileBrowser.labkeyFileBrowser(net)\n", "\n", "def initOrthanc(site,returnFB=False):\n", " net=orthancInterface.orthancInterface()\n", " fconfig=os.path.join(os.path.expanduser('~'),'.labkey','{}.json'.format(site))\n", " net.init(fconfig)\n", " if not returnFB:\n", " return orthancDatabaseBrowser.orthancDB(net)\n", " return orthancDatabaseBrowser.orthancDB(net),orthancFileBrowser.orthancFileBrowser(net)\n", "\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "e7386af6-7b00-4607-a05e-5581db804869", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "User: andrej studen CSRF: f7abf32866c67a6f503e050311032978\n" ] } ], "source": [ "db,fb=initDB('onko-nix',returnFB=True)\n", "odb,ofb=initOrthanc('onko-nix',returnFB=True)" ] }, { "cell_type": "code", "execution_count": 35, "id": "5910e7dd-de77-4260-a23a-78e2a6baabb4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Returning 1\n", "0.0003862617854116126\n", "sliceThickness=2.0 +- 0.0\n", "928\n", "[-389.23828125, -569.73828125, -941.0]\n", "[[1.5234375, 0.0, 0.0], [0.0, 1.5234375, 0.0], [0.0, 0.0, 2.0]]\n", "sliceThickness=3.0 +- 0.0\n", "0\n", "[-403.656, -588.125, -942.0]\n", "[[4.07283, 0.0, 0.0], [0.0, 4.07283, 0.0], [0.0, 0.0, 3.0]]\n" ] } ], "source": [ "def parsePos(x):\n", " return [float(y) for y in x.split('\\\\')]\n", "\n", "def parseOrientation(x):\n", " z=[float(y) for y in x.split('\\\\')]\n", " O=[z[0:3],z[3:6]]\n", " O.append(list(numpy.cross(numpy.array(O[0]),numpy.array(O[1]))))\n", " return O\n", "\n", "def getGeometry(code,odb,seriesId):\n", " #imagepositionpatient 0020-0032\n", " #imageorientationpatient 0020-0037\n", " #pixelSpacing 0028-0030\n", " #instance number 0020-0013\n", " #sliceThickness 0018-0050\n", " \n", " sd=odb.getData('series',seriesId)\n", " firstId=sd['Instances'][0] \n", " \n", " ior=parseOrientation(odb.getDicomField(firstId,'0020-0037'))\n", " #spacing\n", " ips=parsePos(odb.getDicomField(firstId,'0028-0030'))\n", " ist=parsePos(odb.getDicomField(firstId,'0018-0050')) \n", " #don't use slice thickness for pixel spacing\n", " #ips.append(ist[0])\n", " \n", " ipos=[parsePos(odb.getDicomField(y,'0020-0032')) for y in sd['Instances']] \n", " #guess spacing from ipos\n", " m=[numpy.dot(numpy.array(ior[2]),numpy.array(x)) for x in ipos] \n", " #sort array\n", " m1=numpy.sort(m)\n", " dm=m1[1:]-m1[:-1]\n", " zSpc=numpy.average(dm)\n", " zDev=numpy.std(dm)\n", " print(f'sliceThickness={zSpc} +- {zDev}')\n", " ips.append(zSpc)\n", " \n", " #direction, ie. scaled orientation\n", " #copy orientation to direction\n", " xdir=[y for y in ior]\n", " for i in range(3):\n", " xdir[i]=[ips[i]*w for w in xdir[i]]\n", " \n", " #find minimum from original array to idenfify lower slice\n", " im=numpy.argmin(numpy.array(m))\n", " print(im)\n", " org=ipos[im]\n", " print(org)\n", " print(xdir)\n", " return {f'{code}origin':org,f'{code}orientation':xdir}\n", " \n", " \n", " \n", "def getFile(db,odb):\n", " project='/iPNUMMretro/Study'\n", " schema='study'\n", " query='Imaging1'\n", " #coreDir=os.path.join(os.path.expanduser('~'),'temp')\n", " coreDir=os.path.join('Z:')\n", " ds=db.selectRows(project,schema,query,[])\n", " rows=ds['rows'][0:1]\n", " imgs={'CT':'CT_orthancId','PET':'PETWB_orthancId'}\n", " for r in rows:\n", " code=r['pseudoCode']\n", " doUpdate=False\n", " if not code:\n", " code='{:16x}'.format(random.randrange(16**16))\n", " r['pseudoCode']=code\n", " doUpdate=True\n", " \n", " fname=os.path.join(coreDir,f'{code}.npz')\n", " arr={}\n", " #debug don't load array\n", " iSUV={x:getSUVfactor(odb,r[imgs[x]]) for x in imgs}\n", " arr.update({x:iSUV[x]*numpy.load(odb.getNumpy('series',r[imgs[x]])) for x in imgs})\n", " #debug don't calculate geometry\n", " igeo={x:getGeometry(x,odb,r[imgs[x]]) for x in imgs}\n", " #debug skip saving\n", " #continue\n", " _={arr.update(igeo[x]) for x in igeo}\n", " #print(arr)\n", " \n", " \n", " numpy.savez_compressed(fname,**arr)\n", " if doUpdate:\n", " db.modifyRows('update',project,schema,query,[r])\n", " \n", "def mergeDateAndTime(date,time):\n", " time=time.replace(' ','')\n", " dateTimeStr=' '.join([date,time])\n", " #print(f'[{date}] [{time}] [{dateTimeStr}]')\n", " dt=datetime.datetime.strptime(dateTimeStr,'%Y%m%d %H%M%S.%f')\n", " #print(dt)\n", " return dt\n", " \n", "def getSUVfactor(odb,seriesId):\n", " sd=odb.getData('series',seriesId)\n", " orthancId=sd['Instances'][0] \n", " \n", " MODALITY='0008-0060'\n", " PATIENT_WEIGHT='0010-1030'\n", " RADIOPHARMACEUTICAL_STARTTIME='0054-0016/0/0018-1072'\n", " RADIONUCLIDE_TOTALDOSE='0054-0016/0/0018-1074'\n", " RADIONUCLIDE_HALFLIFE='0054-0016/0/0018-1075'\n", " RADIOPHARMACEUTICAL_STARTDATETIME='0054-0016/0/0018-1078'\n", " SERIES_TIME='0008-0031'\n", " SERIES_DATE='0008-0021'\n", " modality=odb.getDicomField(orthancId,MODALITY)\n", " if modality!='PT':\n", " print('Returning 1')\n", " return 1\n", " weight = float(odb.getDicomField(orthancId,PATIENT_WEIGHT))\n", " #print(weight)\n", " halfLife = float(odb.getDicomField(orthancId,RADIONUCLIDE_HALFLIFE))#seconds\n", " #print(halfLife)\n", " injDose = float(odb.getDicomField(orthancId,RADIONUCLIDE_TOTALDOSE))#in Bq\n", " #print(injDose)\n", " #two options for time of activity measurement (full date time or time only, then match it with studyDate)\n", " #injDateTime = odb.getDicomField(orthancId,RADIOPHARMACEUTICAL_STARTDATETIME)\n", " #if troubles:\n", " injDateTime = mergeDateAndTime(odb.getDicomField(orthancId,SERIES_DATE),\n", " odb.getDicomField(orthancId,RADIOPHARMACEUTICAL_STARTTIME))\n", " scanDateTime=mergeDateAndTime(odb.getDicomField(orthancId,SERIES_DATE),\n", " odb.getDicomField(orthancId,SERIES_TIME))\n", " t=(scanDateTime-injDateTime).total_seconds()\n", " scanDose = injDose*numpy.exp(-(numpy.log(2)*t)/halfLife);\n", " SUV_factor = 1/((scanDose/weight)*0.001);\n", " print(SUV_factor)\n", " return SUV_factor\n", "\n", " \n", "getFile(db,odb)\n", " " ] }, { "cell_type": "code", "execution_count": 36, "id": "05cb025f-1c44-4ddf-b859-8b8e9d2584a6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1.5234375, 1.5234375, 2.0]\n", "[1. 0. 0. 0. 1. 0. 0. 0. 1.]\n", "[4.07283, 4.07283, 3.0]\n", "[1. 0. 0. 0. 1. 0. 0. 0. 1.]\n" ] } ], "source": [ "def convertToITK(dt,label):\n", " imgArray=dt[label]\n", " org=dt['{}origin'.format(label)]\n", " orient=dt['{}orientation'.format(label)]\n", " spacing=[numpy.linalg.norm(x) for x in orient]\n", " print(spacing)\n", " orient=[orient[i]/spacing[i] for i in range(3)]\n", " direction=numpy.reshape(orient,9)\n", " print(direction)\n", " img = SimpleITK.GetImageFromArray(imgArray)\n", " img.SetOrigin(org)\n", " img.SetDirection(direction)\n", " img.SetSpacing(spacing)\n", " return img\n", "\n", "#convert to SimpleITKimage to display it\n", "infile=os.path.join('Z:','6ebf549213805ee0.npz')\n", "dt=numpy.load(infile)\n", "ct=convertToITK(dt,'CT')\n", "pet=convertToITK(dt,'PET')\n", "outdir=os.path.join('Z:')\n", "SimpleITK.WriteImage(ct,os.path.join(outdir,'CT.nii.gz'))\n", "SimpleITK.WriteImage(pet,os.path.join(outdir,'PET.nii.gz'))\n", "del ct\n", "del pet\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "6d523d18-ede0-41aa-a93c-96123fa6abbc", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.0" } }, "nbformat": 4, "nbformat_minor": 5 }