-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathretrieve_dicoms.py
executable file
·208 lines (177 loc) · 6.79 KB
/
retrieve_dicoms.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/usr/bin/env python3
import os
import pydicom # pydicom is using the gdcm package for decompression
import sys
import subprocess
import logging
import argparse
import json
from pydicom import dcmread
# MRN is a 7 digit number
# StudyDate format is YYYYMMDD
#
def clean_text(string):
# clean and standardize text descriptions, to make searching files easier
forbidden_symbols = ["*", ".", ",", "\"", "\\", "/", "|", "[", "]", ":", ";", " "]
for symbol in forbidden_symbols:
string = string.replace(symbol, "_") # replace everything with an underscore
return string.lower()
# Initialize logging and set its level
logging.basicConfig()
log = logging.getLogger()
log.setLevel( logging.INFO )
log.setLevel( logging.DEBUG )
# Parse the arguments.
parser = argparse.ArgumentParser(description='Retrieve DICOM files.')
parser.add_argument("--outputDir", required=True)
parser.add_argument("--subjectID", required=True)
parser.add_argument("--studyDate", required=False)
parser.add_argument("--modality", required=False)
parser.add_argument("--aet", required=False, default="PACSDCM", help="The calling Application Entity Title is used to identify a DICOM application")
parser.add_argument("--aec", required=False, default="SYNAPSERESEARCH", help="The called Application Entity Title of the DICOM node that is called.")
parser.add_argument("--namednode", required=False, default="10.20.2.28", help="The DICOM peer that is called.")
parser.add_argument("--dicomport", required=False, default="104", help="The port on the DICOM peer.")
parser.add_argument("--accessionNumberFile", required=False, help="A file to report the discovered accession numbers into.")
args = parser.parse_args()
# user specified parameters
dst = args.outputDir
mrn = args.subjectID
if args.studyDate != None:
StudyDateVar = args.studyDate
else:
StudyDateVar = ""
if args.modality != None:
Modality = args.modality
else:
Modality = ""
if args.accessionNumberFile != None:
accFile = args.accessionNumberFile
else:
accFile = ""
logging.info('Retrieving subject with mrn : %s.' % mrn )
logging.info('Storing DICOM to directory %s.' % dst )
# print("mrn is " + mrn + "\n")
# print("dest is " + dst + "\n")
if not os.path.isdir( dst + '/STUDY_QUERY_INFO'):
os.makedirs(dst+'/STUDY_QUERY_INFO')
# Search the DICOM node for the study info.
# AET: Application Entity Title is used to identify a DICOM application
#AET = 'SYNAPSERESEARCH'
# AEC: The called Application Entity Title of the DICOM node that is called.
#AEC = 'PACSDCM'
# The named node is the DICOM peer that is called.
#NAMEDNODE = 'pacsstor.tch.harvard.edu'
#PORT = 104
# aet: PACSDCM
# namednode: pacsstor.tch.harvard.edu
# dicomport: 104
# aet: SYNAPSERESEARCH
# namednode: 10.20.2.28
# dicomport: 104
# 2BP research
# aet: 2BPMRI_2
# namednode: 10.27.107.244
# dicomport: 104
# Retrieve from Synapse:
# sudo docker run --rm -it --volume `pwd`:/data crl/dicom-tools \
# retrieve_dicoms.py --outputDir . --subjectID NNNNNNN \
# --studyDate YYYYMMDD --aet SYNAPSERESEARCH \
# --aec PACSDCM --namednode pacsstor.tch.harvard.edu
#
AET = args.aet
AEC = args.aec
NAMEDNODE = args.namednode
PORT = args.dicomport
# Create the output directory for the query.
retrieveOutdir = dst + '/STUDY_QUERY_INFO'
if not os.path.isdir( retrieveOutdir ):
os.makedirs(retrieveOutdir)
# Check if the retrieve directory is writable
try:
filehandle = open( retrieveOutdir + '/retrieve.txt', 'w' )
except IOError:
sys.exit( 'Unable to write to file ' + retrieveOutdir )
filehandle.close()
os.remove(retrieveOutdir + '/retrieve.txt')
# Display the command that will be run:
s = " "
print(s.join(["/usr/bin/findscu", "-od", retrieveOutdir,
"--extract", "--show-responses",
"-aet", AET, "-aec", AEC, "--study", "--key", "QueryRetrieveLevel=STUDY",
"--key", 'PatientID=' + mrn,
"--key", 'StudyDate=' + StudyDateVar,
"--key", 'Modality=' + Modality,
"--key", 'StudyInstanceUID',
"--key", 'AccessionNumber',
str(NAMEDNODE), str(PORT)]))
# Search the PACS for studies that match and create rsp*.dcm files:
subprocess.run(["/usr/bin/findscu", "-od", retrieveOutdir,
"--extract", "--show-responses",
"-aet", AET, "-aec", AEC, "--study", "--key", "QueryRetrieveLevel=STUDY",
"--key", 'PatientID=' + mrn,
"--key", 'StudyDate=' + StudyDateVar,
"--key", 'Modality=' + Modality,
"--key", 'StudyInstanceUID',
"--key", 'AccessionNumber',
str(NAMEDNODE), str(PORT)])
# Get the studies from the PACS and write to the output directory:
# Scan the directory and get an iterator of os.DirEntry objects
# corresponding to entries in it using os.scandir() method
obj = os.scandir(retrieveOutdir)
accessionNumberList = []
for entry in obj :
if entry.is_file():
fname = entry.name
print('fname is : ' + fname)
print('path of file is : ' + entry.path)
ds = dcmread(entry.path)
print(ds)
AccessionNumber = ds['AccessionNumber'].value
print('AccessionNumber is :' + str(AccessionNumber))
print('StudyDate searching for :' + str(StudyDateVar))
print('StudyDate is :' + str(ds['StudyDate'].value))
if StudyDateVar != "":
if StudyDateVar != ds['StudyDate'].value:
continue
print('Modality search for : ' + str(Modality))
print('Modality is :' + str(ds['Modality'].value))
if Modality != "":
if Modality != ds['Modality'].value:
continue
outdir = dst + '/' + str(AccessionNumber)
accessionNumberList.append( AccessionNumber )
print(' Retrieving for AccessionNumber :' + str(AccessionNumber) )
print(' Retrieving for AccessionNumber :' + str(AccessionNumber) +
'\nStudyDate is :' + str(ds['StudyDate'].value) +
'\nModality is : ' + ds['Modality'].value + '\n' )
print('DICOM data retrieved to : ' + outdir)
if not os.path.isdir( outdir ):
os.mkdir(outdir)
# Check if the retrieve directory is writable
try:
filehandle = open( outdir + '/retrieve.txt', 'w' )
except IOError:
sys.exit( 'Unable to write to file ' + retrieveOutdir )
filehandle.close()
os.remove(outdir + '/retrieve.txt')
s = " "
print(s.join( ["/usr/bin/getscu",
"--output-directory", outdir,
"--verbose",
"-aet", AET,
"-aec", AEC,
"--study",
str(NAMEDNODE), str(PORT), entry.path ]) )
subprocess.run(["/usr/bin/getscu",
"--output-directory", outdir,
"--verbose",
"-aet", AET,
"-aec", AEC,
"--study",
str(NAMEDNODE), str(PORT), entry.path ])
print('Retrieved accession numbers:')
print(accessionNumberList)
if accFile != "":
with open(accFile, "w") as outfile:
outfile.write( json.dumps(accessionNumberList, indent=4) )
exit()