Skip to content

Commit dc781e3

Browse files
author
Ubuntu
committed
Initial commit.
1 parent d179b5b commit dc781e3

File tree

10 files changed

+3345
-9
lines changed

10 files changed

+3345
-9
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
AHLItoDICOM Module : This class contains the logic to create the AHLI boto3 client.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
"""
6+
import boto3
7+
import tempfile
8+
import logging
9+
10+
11+
12+
class AHLIClientFactory(object):
13+
14+
15+
def __init__(self) -> None:
16+
pass
17+
18+
def __new__(self , aws_access_key : str = None , aws_secret_key : str = None , aws_accendpoint_url : str = None):
19+
try:
20+
session = boto3.Session()
21+
session._loader.search_paths.extend([tempfile.gettempdir()])
22+
AHLIclient = boto3.client('medical-imaging', aws_access_key_id = aws_access_key , aws_secret_access_key = aws_secret_key , endpoint_url=aws_accendpoint_url )
23+
return AHLIclient
24+
except Exception as AHLIErr:
25+
logging.error(f"[AHLIClientFactory] - {AHLIErr}")
26+
return None
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""
2+
AHLItoDICOM Module : This class contains the logic to encapsulate the data and the pixels into a DICOM object.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
"""
6+
from time import sleep
7+
from multiprocessing import Process , Queue
8+
import pydicom
9+
import logging
10+
from pydicom.sequence import Sequence
11+
from pydicom import Dataset , DataElement
12+
from pydicom.dataset import FileDataset, FileMetaDataset
13+
from pydicom.uid import UID
14+
import base64
15+
16+
17+
class AHLIDataDICOMizer():
18+
19+
ds = Dataset()
20+
InstanceId = None
21+
thread_running = True
22+
AHLI_metadata = None
23+
process = None
24+
25+
def __init__(self, InstanceId, AHLI_metadata) -> None:
26+
self.InstanceId = InstanceId
27+
self.DICOMizeJobs = Queue()
28+
self.DICOMizeJobsCompleted = Queue()
29+
self.AHLI_metadata = AHLI_metadata
30+
self.process = Process(target = self.ProcessJobs , args=(self.DICOMizeJobs, self.DICOMizeJobsCompleted, ))
31+
self.process.start()
32+
33+
34+
35+
def AddDICOMizeJob(self,FetchJob):
36+
self.DICOMizeJobs.put(FetchJob)
37+
logging.debug("[AHLIFrameFetcher][AddFetchJob]["+self.InstanceId+"] - Fetch Job added "+str(FetchJob)+".")
38+
39+
def ProcessJobs(self , DICOMizeJobs , DICOMizeJobsCompleted):
40+
while(self.thread_running):
41+
if not DICOMizeJobs.empty():
42+
self.status="busy"
43+
try:
44+
ImageFrame = DICOMizeJobs.get()
45+
vrlist = []
46+
file_meta = FileMetaDataset()
47+
self.ds = FileDataset(None, {}, file_meta=file_meta, preamble=b"\0" * 128)
48+
self.getDICOMVRs(self.AHLI_metadata["Study"]["Series"][ImageFrame["SeriesUID"]]["Instances"][ImageFrame["SOPInstanceUID"]]["DICOMVRs"] , vrlist)
49+
PatientLevel = self.AHLI_metadata["Patient"]["DICOM"]
50+
self.getTags(PatientLevel, self.ds , vrlist)
51+
StudyLevel = self.AHLI_metadata["Study"]["DICOM"]
52+
self.getTags(StudyLevel, self.ds , vrlist)
53+
SeriesLevel=self.AHLI_metadata["Study"]["Series"][ImageFrame["SeriesUID"]]["DICOM"]
54+
self.getTags(SeriesLevel, self.ds , vrlist)
55+
InstanceLevel=self.AHLI_metadata["Study"]["Series"][ImageFrame["SeriesUID"]]["Instances"][ImageFrame["SOPInstanceUID"]]["DICOM"]
56+
self.getTags(InstanceLevel , self.ds , vrlist)
57+
self.ds.file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian
58+
self.ds.is_little_endian = True
59+
self.ds.is_implicit_VR = False
60+
file_meta.MediaStorageSOPInstanceUID = UID(ImageFrame["SOPInstanceUID"])
61+
pixels = ImageFrame["PixelData"]
62+
if (pixels is not None):
63+
self.ds.PixelData = pixels.tobytes()
64+
vrlist.clear()
65+
DICOMizeJobsCompleted.put(self.ds)
66+
except Exception as FetchError:
67+
logging.error(f"[AHLIFrameFetcher][{str(self.InstanceId)}] - {FetchError}")
68+
else:
69+
self.status = 'idle'
70+
sleep(0.05)
71+
72+
def getFramesDICOMized(self):
73+
if not self.DICOMizeJobsCompleted.empty():
74+
obj = self.DICOMizeJobsCompleted.get()
75+
return obj
76+
else:
77+
return None
78+
79+
def getDataset(self):
80+
return self.ds
81+
82+
83+
def getDICOMVRs(self,taglevel, vrlist):
84+
for theKey in taglevel:
85+
vrlist.append( [ theKey , taglevel[theKey] ])
86+
logging.debug(f"[AHLIDataDICOMizer][getDICOMVRs] - List of private tags VRs: {vrlist}\r\n")
87+
88+
89+
90+
def getTags(self,tagLevel, ds , vrlist):
91+
for theKey in tagLevel:
92+
try:
93+
try:
94+
tagvr = pydicom.datadict.dictionary_VR(theKey)
95+
except: #In case the vr is not in the pydicom dictionnary, it might be a private tag , listed in the vrlist
96+
tagvr = None
97+
for vr in vrlist:
98+
if theKey == vr[0]:
99+
tagvr = vr[1]
100+
datavalue=tagLevel[theKey]
101+
#print(f"{theKey} : {datavalue}")
102+
if(tagvr == 'SQ'):
103+
logging.debug(f"{theKey} : {tagLevel[theKey]} , {vrlist}")
104+
seqs = []
105+
for underSeq in tagLevel[theKey]:
106+
seqds = Dataset()
107+
self.getTags(underSeq, seqds, vrlist)
108+
seqs.append(seqds)
109+
datavalue = Sequence(seqs)
110+
continue
111+
if(tagvr == 'US or SS'):
112+
datavalue=tagLevel[theKey]
113+
if (int(datavalue) > 32767):
114+
tagvr = 'US'
115+
if( tagvr in [ 'OB' , 'OD' , 'OF', 'OL', 'OW', 'UN' ] ):
116+
base64_str = tagLevel[theKey]
117+
base64_bytes = base64_str.encode('utf-8')
118+
datavalue = base64.decodebytes(base64_bytes)
119+
if theKey == 'PrivateCreatorID': # Ignore this attribute, otherwise it creates an issue because it doesn't resolve to a DICOM tag
120+
continue
121+
data_element = DataElement(theKey , tagvr , datavalue )
122+
if data_element.tag.group != 2:
123+
try:
124+
if (int(data_element.tag.group) % 2) == 0 : # we are skipping all the private tags
125+
ds.add(data_element)
126+
except:
127+
continue
128+
except Exception as err:
129+
logging.debug(f"[AHLIDataDICOMizer][getTags] - {err}")
130+
continue
131+
132+
def Dispose(self):
133+
self.thread_running = False
134+
self.process.terminate()
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""
2+
AHLItoDICOM Module : This class contains the logic to query the Image pixel raster.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
"""
6+
7+
from multiprocessing import Process , Queue
8+
import logging
9+
from openjpeg import decode
10+
import io
11+
from .AHLIClientFactory import *
12+
13+
14+
class AHLIFrameFetcher:
15+
16+
17+
status = 'idle'
18+
FetchJobs = None
19+
FetchJobsCompleted = None
20+
FetchJobsInError = None
21+
InstanceId= None
22+
client = None
23+
thread_running = True
24+
process = None
25+
aws_access_key = None
26+
aws_secret_key = None
27+
AHLI_endpoint = None
28+
29+
def __init__(self, InstanceId , aws_access_key , aws_secret_key , AHLI_endpoint):
30+
#mp.set_start_method('spawn')
31+
self.InstanceId = InstanceId
32+
self.FetchJobs = Queue()
33+
self.FetchJobsCompleted = Queue()
34+
self.FetchJobsInError = Queue()
35+
self.aws_secret_key = aws_access_key
36+
self.aws_secret_key = aws_secret_key
37+
self.AHLI_endpoint = AHLI_endpoint
38+
self.process = Process(target = self.ProcessJobs , args=(self.FetchJobs,self.FetchJobsCompleted, self.FetchJobsInError , self.aws_access_key , self.aws_secret_key , self.AHLI_endpoint))
39+
self.process.start()
40+
41+
def AddFetchJob(self,FetchJob):
42+
self.FetchJobs.put(FetchJob)
43+
#print(f"[FrameFetcher][{self.InstanceId}] Job entry added")
44+
#print(FetchJob)
45+
logging.debug("[AHLIFrameFetcher][AddFetchJob]["+self.InstanceId+"] - Fetch Job added "+str(FetchJob)+".")
46+
47+
def ProcessJobs(self,FetchJobs : Queue, FetchJobsCompleted : Queue , FetchJobsInError : Queue , aws_access_key : str = None , aws_secret_key : str = None , AHLI_endpoint : str = None):
48+
self.client = AHLIClientFactory( aws_access_key= aws_access_key , aws_secret_key=aws_secret_key , aws_accendpoint_url=AHLI_endpoint )
49+
while(self.thread_running):
50+
if not FetchJobs.empty():
51+
try:
52+
entry = FetchJobs.get(block=False)
53+
entry["PixelData"] = self.curieGetFramePixels(entry["datastoreId"], entry["studyId"], entry["frameId"])
54+
FetchJobsCompleted.put(entry)
55+
except:
56+
FetchJobsInError.put(entry)
57+
58+
59+
60+
def getFramesFetched(self):
61+
if not self.FetchJobsCompleted.empty() :
62+
obj = self.FetchJobsCompleted.get(block=False)
63+
return obj
64+
else:
65+
return None
66+
67+
68+
def curieGetFramePixels(self, datastoreId, studyId, imageFrameId):
69+
try:
70+
res = self.client.get_image_frame(
71+
datastoreId=datastoreId,
72+
imageSetId=studyId,
73+
imageFrameId=imageFrameId)
74+
b = io.BytesIO()
75+
b.write(res['imageFrameBlob'].read())
76+
b.seek(0)
77+
d = decode(b)
78+
return d
79+
except:
80+
logging.info("[AHLIFramefetcher] - Frame could not be decoded.")
81+
return None
82+
83+
def Dispose(self):
84+
self.thread_running = False
85+
self.process.terminate()

0 commit comments

Comments
 (0)