Mercurial > hg > orthanc-tests
comparison Tests/CheckScuTranscoding.py @ 371:6941a4f449cc
CheckScuTranscoding.py
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 21 Jan 2021 17:05:34 +0100 |
parents | |
children | e769bcf2b94f |
comparison
equal
deleted
inserted
replaced
370:7eb5b86508b1 | 371:6941a4f449cc |
---|---|
1 #!/usr/bin/env python | |
2 | |
3 # Orthanc - A Lightweight, RESTful DICOM Store | |
4 # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
5 # Department, University Hospital of Liege, Belgium | |
6 # Copyright (C) 2017-2021 Osimis S.A., Belgium | |
7 # | |
8 # This program is free software: you can redistribute it and/or | |
9 # modify it under the terms of the GNU General Public License as | |
10 # published by the Free Software Foundation, either version 3 of the | |
11 # License, or (at your option) any later version. | |
12 # | |
13 # This program is distributed in the hope that it will be useful, but | |
14 # WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 # General Public License for more details. | |
17 # | |
18 # You should have received a copy of the GNU General Public License | |
19 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | |
21 | |
22 import json | |
23 import os | |
24 import subprocess | |
25 import sys | |
26 import time | |
27 import Toolbox | |
28 | |
29 | |
30 if len(sys.argv) < 2: | |
31 print('Must provide a path to Orthanc binaries') | |
32 exit(-1) | |
33 | |
34 | |
35 TMP = '/tmp/OrthancTest' | |
36 TMP_ORTHANC = os.path.join(TMP, 'orthanc') | |
37 TMP_STORESCP = os.path.join(TMP, 'storescp') | |
38 | |
39 CONFIG = os.path.join(TMP, 'orthanc', 'Configuration.json') | |
40 ORTHANC = Toolbox.DefineOrthanc() | |
41 | |
42 if os.path.exists(TMP): | |
43 print('Temporary path already exists: %s' % TMP) | |
44 exit(-1) | |
45 | |
46 os.mkdir(TMP) | |
47 os.mkdir(TMP_ORTHANC) | |
48 os.mkdir(TMP_STORESCP) | |
49 | |
50 | |
51 def DropOrthanc(): | |
52 while True: | |
53 try: | |
54 instances = Toolbox.DoGet(ORTHANC, '/instances') | |
55 if len(instances) == 0: | |
56 break | |
57 else: | |
58 for i in instances: | |
59 Toolbox.DoDelete(ORTHANC, '/instances/%s' % i) | |
60 except: | |
61 # Orthanc is still in its startup process, wait for it to | |
62 # become available | |
63 time.sleep(0.05) | |
64 | |
65 | |
66 def CreateStorescpConfiguration(acceptedSyntaxes): | |
67 with open(os.path.join(TMP_STORESCP, 'config'), 'w') as f: | |
68 f.write('[[TransferSyntaxes]]\n') | |
69 | |
70 f.write('[Accepted]\n') | |
71 for i in range(len(acceptedSyntaxes)): | |
72 f.write('TransferSyntax%d = %s\n' % (i + 1, acceptedSyntaxes[i])) | |
73 | |
74 f.write('[[PresentationContexts]]\n') | |
75 f.write('[StorageSCP]\n') | |
76 | |
77 # These strings correspond to the SOP class UIDs of the DICOM | |
78 # instances in folder "../Database/TransferSyntaxes/" | |
79 SOP_CLASS_UIDS = [ | |
80 '1.2.840.10008.5.1.4.1.1.2', | |
81 '1.2.840.10008.5.1.4.1.1.4', | |
82 '1.2.840.10008.5.1.4.1.1.6', | |
83 '1.2.840.10008.5.1.4.1.1.6.1', | |
84 '1.2.840.10008.5.1.4.1.1.7', | |
85 ] | |
86 | |
87 for i in range(len(SOP_CLASS_UIDS)): | |
88 f.write('PresentationContext%d = %s\\Accepted\n' % (i + 1, SOP_CLASS_UIDS[i])) | |
89 | |
90 f.write('[[Profiles]]\n') | |
91 f.write('[Default]\n') | |
92 f.write('PresentationContexts = StorageSCP\n') | |
93 | |
94 | |
95 def TestStore(config, storescpArgs, tests): | |
96 config['DicomModalities'] = { | |
97 'storescp' : [ 'STORESCP', 'localhost', 2000 ] | |
98 } | |
99 | |
100 with open(CONFIG, 'w') as f: | |
101 f.write(json.dumps(config)) | |
102 | |
103 FNULL = open(os.devnull, 'w') # Emulates "subprocess.DEVNULL" on Python 2.7 | |
104 process1 = subprocess.Popen( | |
105 sys.argv[1:] + [ CONFIG, '--no-jobs' ], #, '--trace-dicom' ], | |
106 cwd = TMP_ORTHANC, | |
107 #stdout=FNULL, | |
108 stderr=FNULL, | |
109 #shell=True | |
110 ) | |
111 | |
112 process2 = subprocess.Popen( | |
113 [ 'storescp', '-p', '2000' ] + storescpArgs, | |
114 cwd = TMP_STORESCP, | |
115 #stdout=FNULL, | |
116 #stderr=FNULL, | |
117 #shell=True | |
118 ) | |
119 | |
120 success = True | |
121 | |
122 try: | |
123 for test in tests: | |
124 DropOrthanc() | |
125 for f in os.listdir(TMP_STORESCP): | |
126 os.remove(os.path.join(TMP_STORESCP, f)) | |
127 | |
128 i = Toolbox.UploadInstance(ORTHANC, test[0]) ['ID'] | |
129 | |
130 try: | |
131 Toolbox.DoPost(ORTHANC, '/modalities/storescp/store', { | |
132 'Resources' : [ i ], | |
133 'Synchronous' : True, | |
134 }) | |
135 except: | |
136 if test[1] != None: | |
137 print('INTERNAL ERROR on: %s' % test[0]) | |
138 success = False | |
139 continue | |
140 | |
141 f = os.listdir(TMP_STORESCP) | |
142 if len(f) > 1: | |
143 print('INTERNAL ERROR') | |
144 success = False | |
145 elif len(f) == 0: | |
146 if test[1] != None: | |
147 print('No file was received by storescp! %s' % test[0]) | |
148 success = False | |
149 else: | |
150 if test[1] == None: | |
151 print('No file should have been received by storescp! %s' % test[0]) | |
152 success = False | |
153 else: | |
154 with open(os.path.join(TMP_STORESCP, f[0]), 'rb') as f: | |
155 ts = Toolbox.GetTransferSyntax(f.read()) | |
156 | |
157 if ts != test[1]: | |
158 print('TRANSFER SYNTAX MISMATCH: observed %s vs. expected %s' % (ts, test[1])) | |
159 success = False | |
160 | |
161 except Exception as e: | |
162 print('EXCEPTION: %s' % e) | |
163 success = False | |
164 | |
165 process1.terminate() | |
166 process2.terminate() | |
167 | |
168 process1.wait() | |
169 process2.wait() | |
170 | |
171 return success | |
172 | |
173 | |
174 def Assert(b): | |
175 if not b: | |
176 raise Exception('Bad result') | |
177 | |
178 | |
179 | |
180 ## | |
181 ## Each test specifies: The input DICOM instance, and the expected | |
182 ## transfer syntax as received by storescp | |
183 ## | |
184 | |
185 | |
186 print('==== TEST 1 ====') | |
187 Assert(TestStore( | |
188 { | |
189 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.1', # Little Endian Explicit | |
190 }, | |
191 [ '+xa' ], # storescp accepts any transfer syntax | |
192 # (DicomScuPreferredTransferSyntax has no effect) | |
193 [ | |
194 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
195 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
196 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
197 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.51'), | |
198 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
199 ])) | |
200 | |
201 | |
202 print('==== TEST 2 ====') | |
203 Assert(TestStore( | |
204 { | |
205 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.2', # Big Endian | |
206 }, | |
207 [ '+xa' ], # storescp accepts any transfer syntax | |
208 # (DicomScuPreferredTransferSyntax has no effect) | |
209 [ | |
210 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
211 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
212 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
213 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.51'), | |
214 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
215 ])) | |
216 | |
217 | |
218 print('==== TEST 3 ====') | |
219 Assert(TestStore( | |
220 { | |
221 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70', # JPEG baseline 12bpp | |
222 }, | |
223 [ '+xa' ], # storescp accepts any transfer syntax | |
224 # (DicomScuPreferredTransferSyntax has no effect) | |
225 [ | |
226 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
227 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
228 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
229 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.51'), | |
230 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
231 ])) | |
232 | |
233 | |
234 print('==== TEST 4 ====') | |
235 Assert(TestStore( | |
236 { | |
237 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.1', # Little Endian Explicit | |
238 }, | |
239 [ ], # storescp only accepts uncompressed transfer syntaxes | |
240 [ | |
241 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
242 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
243 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
244 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.1'), | |
245 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.1'), | |
246 ])) | |
247 | |
248 | |
249 print('==== TEST 5 ====') | |
250 Assert(TestStore( | |
251 { | |
252 # Defaults to "1.2.840.10008.1.2.1", Little Endian Explicit | |
253 # (was Little Endian Implicit in Orthanc between 1.7.0 and 1.8.2) | |
254 }, | |
255 [ ], # storescp only accepts uncompressed transfer syntaxes | |
256 [ | |
257 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
258 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
259 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
260 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.1'), | |
261 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.1'), | |
262 ])) | |
263 | |
264 | |
265 print('==== TEST 6 ====') | |
266 Assert(TestStore( | |
267 { | |
268 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2', # Little Endian Implicit | |
269 }, | |
270 [ ], # storescp only accepts uncompressed transfer syntaxes | |
271 [ | |
272 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
273 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
274 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
275 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2'), | |
276 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2'), | |
277 ])) | |
278 | |
279 | |
280 print('==== TEST 7 ====') | |
281 Assert(TestStore( | |
282 { | |
283 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.2', # Big Endian | |
284 }, | |
285 [ ], # storescp only accepts uncompressed transfer syntaxes | |
286 [ | |
287 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
288 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
289 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
290 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.2'), | |
291 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.2'), | |
292 ])) | |
293 | |
294 | |
295 print('==== TEST 8 ====') | |
296 Assert(TestStore( | |
297 { | |
298 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
299 }, | |
300 [ ], # storescp only accepts uncompressed transfer syntaxes, | |
301 # Little Endian Explicit will be chosed by Orthanc (was | |
302 # Little Endian Implicit in Orthanc between 1.7.0 and 1.8.2) | |
303 [ | |
304 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
305 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
306 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
307 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.1'), | |
308 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.1'), | |
309 ])) | |
310 | |
311 | |
312 print('==== TEST 9 ====') | |
313 Assert(TestStore( | |
314 { | |
315 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
316 }, | |
317 [ '+xi' ], # storescp only accepts Little Endian Implicit (1.2.840.10008.1.2) | |
318 [ | |
319 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
320 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2'), | |
321 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2'), | |
322 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2'), | |
323 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2'), | |
324 ])) | |
325 | |
326 | |
327 print('==== TEST 10 ====') | |
328 CreateStorescpConfiguration([ | |
329 '1.2.840.10008.1.2.4.70', | |
330 ]) | |
331 Assert(TestStore( | |
332 { | |
333 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
334 }, | |
335 [ '-xf', 'config', 'Default' ], # storescp only accepts "1.2.840.10008.1.2.4.70" | |
336 [ | |
337 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2.4.70'), | |
338 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.4.70'), | |
339 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.4.70'), | |
340 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.70'), | |
341 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
342 ])) | |
343 | |
344 | |
345 print('==== TEST 11 ====') | |
346 CreateStorescpConfiguration([ | |
347 '1.2.840.10008.1.2.4.57', | |
348 ]) | |
349 Assert(TestStore( | |
350 { | |
351 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
352 }, | |
353 [ '-xf', 'config', 'Default' ], | |
354 [ | |
355 ('TransferSyntaxes/1.2.840.10008.1.2.4.57.dcm', '1.2.840.10008.1.2.4.57'), | |
356 | |
357 # All the transfers below will be rejected by storescp | |
358 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', None), | |
359 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', None), | |
360 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', None), | |
361 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', None), | |
362 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', None), | |
363 ])) | |
364 | |
365 | |
366 print('==== TEST 12 ====') | |
367 CreateStorescpConfiguration([ | |
368 '1.2.840.10008.1.2.4.70', | |
369 '1.2.840.10008.1.2.1', | |
370 ]) | |
371 Assert(TestStore( | |
372 { | |
373 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
374 }, | |
375 [ '-xf', 'config', 'Default' ], | |
376 [ | |
377 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2.4.70'), | |
378 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
379 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.4.70'), | |
380 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.70'), | |
381 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
382 ])) | |
383 | |
384 | |
385 print('==== TEST 13 ====') | |
386 CreateStorescpConfiguration([ | |
387 '1.2.840.10008.1.2.4.90', | |
388 '1.2.840.10008.1.2.1', | |
389 ]) | |
390 Assert(TestStore( | |
391 { | |
392 # The built-in DCMTK transcoder of Orthanc cannot transcode to | |
393 # JPEG2k, so the fallback "1.2.840.10008.1.2.1" transfer | |
394 # syntax will be used if transcoding is needed | |
395 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.90' | |
396 }, | |
397 [ '-xf', 'config', 'Default' ], | |
398 [ | |
399 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2.1'), | |
400 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
401 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.1'), | |
402 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.1'), | |
403 ('TransferSyntaxes/1.2.840.10008.1.2.4.90.dcm', '1.2.840.10008.1.2.4.90'), | |
404 ])) | |
405 | |
406 | |
407 print('Success!') |