annotate Resources/GenerateAnonymizationProfile.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 94f4a18a79cc
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2312
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
1 #!/usr/bin/env python
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
2
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
3 # Orthanc - A Lightweight, RESTful DICOM Store
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
4 # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
5 # Department, University Hospital of Liege, Belgium
3060
4e43e67f8ecf preparing for 2019
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2506
diff changeset
6 # Copyright (C) 2017-2019 Osimis S.A., Belgium
2312
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
7 #
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
8 # This program is free software: you can redistribute it and/or
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
9 # modify it under the terms of the GNU General Public License as
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
10 # published by the Free Software Foundation, either version 3 of the
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
11 # License, or (at your option) any later version.
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
12 #
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
13 # This program is distributed in the hope that it will be useful, but
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
16 # General Public License for more details.
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
17 #
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
18 # You should have received a copy of the GNU General Public License
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
20
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
21
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
22 import re
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
23 import sys
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
24 import xml.etree.ElementTree as ET
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
25
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
26 # Usage:
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
27 # ./GenerateAnonymizationProfile.py ~/Subversion/dicom-specification/2017c/part15.xml
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
28
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
29 if len(sys.argv) != 2:
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
30 raise Exception('Please provide the path to the part15.xml file from the DICOM standard')
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
31
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
32 with open(sys.argv[1], 'r') as f:
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
33 root = ET.fromstring(f.read())
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
34
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
35 br = '{http://docbook.org/ns/docbook}' # Shorthand variable
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
36
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
37
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
38 LINES = []
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
39
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
40 def FormatLine(command, name):
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
41 indentation = 65
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
42
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
43 if len(command) > indentation:
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
44 raise Exception('Too long command')
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
45
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
46 line = ' ' + command + (' ' * (indentation - len(command))) + '// ' + name
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
47 LINES.append(line)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
48
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
49 def FormatUnknown(rawTag, name, profile):
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
50 FormatLine('// TODO: %s with rule %s' % (rawTag, profile), name)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
51
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
52
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
53 RAW_TAG_RE = re.compile(r'^\(\s*([0-9A-F]{4})\s*,\s*([0-9A-F]{4})\s*\)$')
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
54
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
55
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
56 for table in root.iter('%stable' % br):
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
57 if table.attrib['label'] == 'E.1-1':
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
58 for row in table.find('%stbody' % br).iter('%str' % br):
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
59 rawTag = row.find('%std[2]/%spara' % (br, br)).text
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
60 name = row.find('%std[1]/%spara' % (br, br)).text
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
61 profile = row.find('%std[5]/%spara' % (br, br)).text
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
62
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
63 if len(name.strip()) == 0:
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
64 continue
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
65
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
66 match = RAW_TAG_RE.match(rawTag)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
67 if match == None:
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
68 FormatUnknown(rawTag, name, profile)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
69 else:
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
70 tag = '0x%s, 0x%s' % (match.group(1).lower(), match.group(2).lower())
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
71
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
72 if name in [
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
73 'SOP Instance UID',
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
74 'Series Instance UID',
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
75 'Study Instance UID',
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
76 ]:
2506
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
77 FormatLine('// Tag (%s) is set in Apply() /* %s */' % (tag, profile), name)
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
78 elif name in [
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
79 'Referenced Image Sequence',
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
80 'Source Image Sequence',
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
81 'Referenced SOP Instance UID',
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
82 'Frame of Reference UID',
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
83 'Referenced Frame of Reference UID',
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
84 'Related Frame of Reference UID',
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
85 ]:
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
86 FormatLine('// Tag (%s) => RelationshipsVisitor /* %s */' % (tag, profile), name)
2313
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
87 elif name in [
2312
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
88 'Patient\'s Name',
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
89 'Patient ID',
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
90 ]:
2506
51b91ead6c38 Preservation of UID relationships while anonymizing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2447
diff changeset
91 FormatLine('// Tag (%s) is set below (*) /* %s */' % (tag, profile), name)
2312
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
92 elif profile == 'X':
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
93 FormatLine('removals_.insert(DicomTag(%s));' % tag, name)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
94 elif profile.startswith('X/'):
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
95 FormatLine('removals_.insert(DicomTag(%s)); /* %s */' % (tag, profile), name)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
96 elif profile == 'Z':
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
97 FormatLine('clearings_.insert(DicomTag(%s));' % tag, name)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
98 elif profile == 'D' or profile.startswith('Z/'):
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
99 FormatLine('clearings_.insert(DicomTag(%s)); /* %s */' % (tag, profile), name)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
100 elif profile == 'U':
2313
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
101 FormatLine('removals_.insert(DicomTag(%s)); /* TODO UID */' % (tag), name)
2312
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
102 else:
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
103 FormatUnknown(rawTag, name, profile)
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
104
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
105 for line in sorted(LINES):
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
106 print line
8700dcaa02e5 GenerateAnonymizationProfile.py
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
107
2313
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
108
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
109 # D - replace with a non-zero length value that may be a dummy value and consistent with the VR
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
110 # Z - replace with a zero length value, or a non-zero length value that may be a dummy value and consistent with the VR
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
111 # X - remove
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
112 # K - keep (unchanged for non-sequence attributes, cleaned for sequences)
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
113 # C - clean, that is replace with values of similar meaning known not to contain identifying information and consistent with the VR
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
114 # U - replace with a non-zero length UID that is internally consistent within a set of Instances
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
115 # Z/D - Z unless D is required to maintain IOD conformance (Type 2 versus Type 1)
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
116 # X/Z - X unless Z is required to maintain IOD conformance (Type 3 versus Type 2)
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
117 # X/D - X unless D is required to maintain IOD conformance (Type 3 versus Type 1)
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
118 # X/Z/D - X unless Z or D is required to maintain IOD conformance (Type 3 versus Type 2 versus Type 1)
d19e716b79fa switch to anonymization according to DICOM 2017c
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 2312
diff changeset
119 # X/Z/U* - X unless Z or replacement of contained instance UIDs (U) is required to maintain IOD conformance (Type 3 versus Type 2 versus Type 1 sequences containing UID references)