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