annotate Resources/Samples/Python/AutoClassify.py @ 3103:81b58b549845

back to using 'var' instead of 'let' since let is not supported by many old browsers. All variables declaration have been moved to the top of the function to better show that their scope is the function
author Alain Mazy <alain@mazy.be>
date Thu, 10 Jan 2019 10:51:36 +0100
parents 4e43e67f8ecf
children d00db444a3a3
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
1 #!/usr/bin/python
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
2 # -*- coding: utf-8 -*-
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
3
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
4 # Orthanc - A Lightweight, RESTful DICOM Store
1900
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1288
diff changeset
5 # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
1288
6e7e5ed91c2d upgrade to year 2015
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1184
diff changeset
6 # Department, University Hospital of Liege, Belgium
3060
4e43e67f8ecf preparing for 2019
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
7 # Copyright (C) 2017-2019 Osimis S.A., Belgium
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
8 #
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
9 # This program is free software: you can redistribute it and/or
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
10 # modify it under the terms of the GNU General Public License as
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
11 # published by the Free Software Foundation, either version 3 of the
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
12 # License, or (at your option) any later version.
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
13 #
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
14 # This program is distributed in the hope that it will be useful, but
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
17 # General Public License for more details.
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
18 #
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
19 # You should have received a copy of the GNU General Public License
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
21
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
22
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
23 import argparse
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
24 import time
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
25 import os
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
26 import os.path
1184
4e9d517503ae port to Python3
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1183
diff changeset
27 import sys
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
28 import RestToolbox
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
29
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
30 parser = argparse.ArgumentParser(
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
31 description = 'Automated classification of DICOM files from Orthanc.',
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
32 formatter_class = argparse.ArgumentDefaultsHelpFormatter)
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
33
2032
65b1ce7cb84f Replaced "localhost" by "127.0.0.1", as it might impact performance on Windows
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1900
diff changeset
34 parser.add_argument('--host', default = '127.0.0.1',
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
35 help = 'The host address that runs Orthanc')
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
36 parser.add_argument('--port', type = int, default = '8042',
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
37 help = 'The port number to which Orthanc is listening for the REST API')
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
38 parser.add_argument('--target', default = 'OrthancFiles',
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
39 help = 'The target directory where to store the DICOM files')
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
40 parser.add_argument('--all', action = 'store_true',
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
41 help = 'Replay the entire history on startup (disabled by default)')
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
42 parser.set_defaults(all = False)
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
43 parser.add_argument('--remove', action = 'store_true',
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
44 help = 'Remove DICOM files from Orthanc once classified (disabled by default)')
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
45 parser.set_defaults(remove = False)
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
46
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
47
1183
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
48 def FixPath(p):
2070
7e6afa0beaf6 samples: improved handling of special characters in paths
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2032
diff changeset
49 return p.encode('ascii', errors = 'replace').translate(None, r"'\/:*?\"<>|!=").strip()
1183
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
50
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
51 def GetTag(resource, tag):
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
52 if ('MainDicomTags' in resource and
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
53 tag in resource['MainDicomTags']):
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
54 return resource['MainDicomTags'][tag]
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
55 else:
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
56 return 'No' + tag
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
57
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
58 def ClassifyInstance(instanceId):
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
59 # Extract the patient, study, series and instance information
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
60 instance = RestToolbox.DoGet('%s/instances/%s' % (URL, instanceId))
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
61 series = RestToolbox.DoGet('%s/series/%s' % (URL, instance['ParentSeries']))
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
62 study = RestToolbox.DoGet('%s/studies/%s' % (URL, series['ParentStudy']))
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
63 patient = RestToolbox.DoGet('%s/patients/%s' % (URL, study['ParentPatient']))
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
64
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
65 # Construct a target path
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
66 a = '%s - %s' % (GetTag(patient, 'PatientID'),
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
67 GetTag(patient, 'PatientName'))
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
68 b = GetTag(study, 'StudyDescription')
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
69 c = '%s - %s' % (GetTag(series, 'Modality'),
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
70 GetTag(series, 'SeriesDescription'))
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
71 d = '%s.dcm' % GetTag(instance, 'SOPInstanceUID')
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
72
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
73 p = os.path.join(args.target, FixPath(a), FixPath(b), FixPath(c))
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
74 f = os.path.join(p, FixPath(d))
1184
4e9d517503ae port to Python3
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1183
diff changeset
75
1183
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
76 # Copy the DICOM file to the target path
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
77 print('Writing new DICOM file: %s' % f)
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
78
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
79 try:
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
80 os.makedirs(p)
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
81 except:
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
82 # Already existing directory, ignore the error
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
83 pass
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
84
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
85 dcm = RestToolbox.DoGet('%s/instances/%s/file' % (URL, instanceId))
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
86 with open(f, 'wb') as g:
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
87 g.write(dcm)
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
88
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
89
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
90 # Parse the arguments
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
91 args = parser.parse_args()
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
92 URL = 'http://%s:%d' % (args.host, args.port)
1183
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
93 print('Connecting to Orthanc on address: %s' % URL)
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
94
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
95 # Compute the starting point for the changes loop
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
96 if args.all:
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
97 current = 0
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
98 else:
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
99 current = RestToolbox.DoGet(URL + '/changes?last')['Last']
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
100
1183
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
101 # Polling loop using the 'changes' API of Orthanc, waiting for the
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
102 # incoming of new DICOM files
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
103 while True:
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
104 r = RestToolbox.DoGet(URL + '/changes', {
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
105 'since' : current,
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
106 'limit' : 4 # Retrieve at most 4 changes at once
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
107 })
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
108
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
109 for change in r['Changes']:
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
110 # We are only interested interested in the arrival of new instances
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
111 if change['ChangeType'] == 'NewInstance':
1183
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
112 try:
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
113 ClassifyInstance(change['ID'])
2075
bec08e9236f5 more robust AutoClassify.py sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2070
diff changeset
114
bec08e9236f5 more robust AutoClassify.py sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2070
diff changeset
115 # If requested, remove the instance once it has been
bec08e9236f5 more robust AutoClassify.py sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2070
diff changeset
116 # properly handled by "ClassifyInstance()". Thanks to
bec08e9236f5 more robust AutoClassify.py sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2070
diff changeset
117 # the "try/except" block, the instance is not removed
bec08e9236f5 more robust AutoClassify.py sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2070
diff changeset
118 # if the "ClassifyInstance()" function fails.
bec08e9236f5 more robust AutoClassify.py sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2070
diff changeset
119 if args.remove:
bec08e9236f5 more robust AutoClassify.py sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2070
diff changeset
120 RestToolbox.DoDelete('%s/instances/%s' % (URL, change['ID']))
bec08e9236f5 more robust AutoClassify.py sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2070
diff changeset
121
1183
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
122 except:
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
123 print('Unable to write instance %s to the disk' % change['ID'])
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
124
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
125 current = r['Last']
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
126
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
127 if r['Done']:
1183
6ef2c81581cd more fault-tolerant sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 1182
diff changeset
128 print('Everything has been processed: Waiting...')
1182
d74ac5d0bcaf New sample: Automated classification of DICOM files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
129 time.sleep(1)