comparison OrthancServer/DicomModification.cpp @ 786:b6d6b65142e8

DicomModification
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 05 May 2014 13:07:10 +0200
parents
children ac18946afa74
comparison
equal deleted inserted replaced
785:7cbed653476f 786:b6d6b65142e8
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
4 * Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #include "DicomModification.h"
34
35 #include "../Core/OrthancException.h"
36
37 namespace Orthanc
38 {
39 void DicomModification::MapDicomIdentifier(ParsedDicomFile& dicom,
40 DicomRootLevel level)
41 {
42 std::auto_ptr<DicomTag> tag;
43
44 switch (level)
45 {
46 case DicomRootLevel_Study:
47 tag.reset(new DicomTag(DICOM_TAG_STUDY_INSTANCE_UID));
48 break;
49
50 case DicomRootLevel_Series:
51 tag.reset(new DicomTag(DICOM_TAG_SERIES_INSTANCE_UID));
52 break;
53
54 case DicomRootLevel_Instance:
55 tag.reset(new DicomTag(DICOM_TAG_SOP_INSTANCE_UID));
56 break;
57
58 default:
59 throw OrthancException(ErrorCode_InternalError);
60 }
61
62 std::string original;
63 if (!dicom.GetTagValue(original, *tag))
64 {
65 original = "";
66 }
67
68 std::string mapped;
69
70 UidMap::const_iterator previous = uidMap_.find(std::make_pair(level, original));
71 if (previous == uidMap_.end())
72 {
73 mapped = FromDcmtkBridge::GenerateUniqueIdentifier(level);
74 uidMap_.insert(std::make_pair(std::make_pair(level, original), mapped));
75 }
76 else
77 {
78 mapped = previous->second;
79 }
80
81 dicom.Replace(*tag, mapped);
82 }
83
84 DicomModification::DicomModification()
85 {
86 removePrivateTags_ = false;
87 level_ = DicomRootLevel_Instance;
88 }
89
90 void DicomModification::Reset(const DicomTag& tag)
91 {
92 removals_.erase(tag);
93 replacements_.erase(tag);
94 }
95
96 void DicomModification::Remove(const DicomTag& tag)
97 {
98 removals_.insert(tag);
99 replacements_.erase(tag);
100 }
101
102 bool DicomModification::IsRemoved(const DicomTag& tag) const
103 {
104 return removals_.find(tag) != removals_.end();
105 }
106
107 void DicomModification::Replace(const DicomTag& tag,
108 const std::string& value)
109 {
110 removals_.erase(tag);
111 replacements_[tag] = value;
112 }
113
114 bool DicomModification::IsReplaced(const DicomTag& tag) const
115 {
116 return replacements_.find(tag) != replacements_.end();
117 }
118
119 const std::string& DicomModification::GetReplacement(const DicomTag& tag) const
120 {
121 Replacements::const_iterator it = replacements_.find(tag);
122
123 if (it == replacements_.end())
124 {
125 throw OrthancException(ErrorCode_InexistentItem);
126 }
127 else
128 {
129 return it->second;
130 }
131 }
132
133 void DicomModification::SetRemovePrivateTags(bool removed)
134 {
135 removePrivateTags_ = removed;
136 }
137
138 void DicomModification::SetLevel(DicomRootLevel level)
139 {
140 uidMap_.clear();
141 level_ = level;
142 }
143
144 void DicomModification::SetupAnonymization()
145 {
146 removals_.clear();
147 replacements_.clear();
148 removePrivateTags_ = true;
149 level_ = DicomRootLevel_Patient;
150 uidMap_.clear();
151
152 // This is Table E.1-1 from PS 3.15-2008 - DICOM Part 15: Security and System Management Profiles
153 removals_.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID
154 //removals_.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set in Apply()
155 removals_.insert(DicomTag(0x0008, 0x0050)); // Accession Number
156 removals_.insert(DicomTag(0x0008, 0x0080)); // Institution Name
157 removals_.insert(DicomTag(0x0008, 0x0081)); // Institution Address
158 removals_.insert(DicomTag(0x0008, 0x0090)); // Referring Physician's Name
159 removals_.insert(DicomTag(0x0008, 0x0092)); // Referring Physician's Address
160 removals_.insert(DicomTag(0x0008, 0x0094)); // Referring Physician's Telephone Numbers
161 removals_.insert(DicomTag(0x0008, 0x1010)); // Station Name
162 removals_.insert(DicomTag(0x0008, 0x1030)); // Study Description
163 removals_.insert(DicomTag(0x0008, 0x103e)); // Series Description
164 removals_.insert(DicomTag(0x0008, 0x1040)); // Institutional Department Name
165 removals_.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of Record
166 removals_.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians' Name
167 removals_.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study
168 removals_.insert(DicomTag(0x0008, 0x1070)); // Operators' Name
169 removals_.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description
170 removals_.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID
171 removals_.insert(DicomTag(0x0008, 0x2111)); // Derivation Description
172 //removals_.insert(DicomTag(0x0010, 0x0010)); // Patient's Name => cf. below (*)
173 //removals_.insert(DicomTag(0x0010, 0x0020)); // Patient ID => cf. below (*)
174 removals_.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date
175 removals_.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time
176 removals_.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex
177 removals_.insert(DicomTag(0x0010, 0x1000)); // Other Patient Ids
178 removals_.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names
179 removals_.insert(DicomTag(0x0010, 0x1010)); // Patient's Age
180 removals_.insert(DicomTag(0x0010, 0x1020)); // Patient's Size
181 removals_.insert(DicomTag(0x0010, 0x1030)); // Patient's Weight
182 removals_.insert(DicomTag(0x0010, 0x1090)); // Medical Record Locator
183 removals_.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group
184 removals_.insert(DicomTag(0x0010, 0x2180)); // Occupation
185 removals_.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History
186 removals_.insert(DicomTag(0x0010, 0x4000)); // Patient Comments
187 removals_.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number
188 removals_.insert(DicomTag(0x0018, 0x1030)); // Protocol Name
189 //removals_.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => set in Apply()
190 //removals_.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => set in Apply()
191 removals_.insert(DicomTag(0x0020, 0x0010)); // Study ID
192 removals_.insert(DicomTag(0x0020, 0x0052)); // Frame of Reference UID
193 removals_.insert(DicomTag(0x0020, 0x0200)); // Synchronization Frame of Reference UID
194 removals_.insert(DicomTag(0x0020, 0x4000)); // Image Comments
195 removals_.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence
196 removals_.insert(DicomTag(0x0040, 0xa124)); // UID
197 removals_.insert(DicomTag(0x0040, 0xa730)); // Content Sequence
198 removals_.insert(DicomTag(0x0088, 0x0140)); // Storage Media File-set UID
199 removals_.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID
200 removals_.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID
201
202 // Some more removals (from the experience of DICOM files at the CHU of Liege)
203 removals_.insert(DicomTag(0x0010, 0x1040)); // Patient's Address
204 removals_.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician
205 removals_.insert(DicomTag(0x0010, 0x2154)); // PatientTelephoneNumbers
206 removals_.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts
207
208 // Set the DeidentificationMethod tag
209 replacements_.insert(std::make_pair(DicomTag(0x0012, 0x0063), "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1"));
210
211 // Set the PatientIdentityRemoved tag
212 replacements_.insert(std::make_pair(DicomTag(0x0012, 0x0062), "YES"));
213
214 // (*) Choose a random patient name and ID
215 std::string patientId = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Patient);
216 replacements_[DICOM_TAG_PATIENT_ID] = patientId;
217 replacements_[DICOM_TAG_PATIENT_NAME] = patientId;
218 }
219
220 void DicomModification::Apply(ParsedDicomFile& toModify)
221 {
222 // Check the request
223 assert(DicomRootLevel_Patient + 1 == DicomRootLevel_Study &&
224 DicomRootLevel_Study + 1 == DicomRootLevel_Series &&
225 DicomRootLevel_Series + 1 == DicomRootLevel_Instance);
226
227 if (IsRemoved(DICOM_TAG_PATIENT_ID) ||
228 IsRemoved(DICOM_TAG_STUDY_INSTANCE_UID) ||
229 IsRemoved(DICOM_TAG_SERIES_INSTANCE_UID) ||
230 IsRemoved(DICOM_TAG_SOP_INSTANCE_UID))
231 {
232 throw OrthancException(ErrorCode_BadRequest);
233 }
234
235 if (level_ == DicomRootLevel_Patient && !IsReplaced(DICOM_TAG_PATIENT_ID))
236 {
237 throw OrthancException(ErrorCode_BadRequest);
238 }
239
240 if (level_ > DicomRootLevel_Patient && IsReplaced(DICOM_TAG_PATIENT_ID))
241 {
242 throw OrthancException(ErrorCode_BadRequest);
243 }
244
245 if (level_ > DicomRootLevel_Study && IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID))
246 {
247 throw OrthancException(ErrorCode_BadRequest);
248 }
249
250 if (level_ > DicomRootLevel_Series && IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID))
251 {
252 throw OrthancException(ErrorCode_BadRequest);
253 }
254
255 // (1) Remove the private tags, if need be
256 if (removePrivateTags_)
257 {
258 toModify.RemovePrivateTags();
259 }
260
261 // (2) Remove the tags specified by the user
262 for (Removals::const_iterator it = removals_.begin();
263 it != removals_.end(); ++it)
264 {
265 toModify.Remove(*it);
266 }
267
268 // (3) Replace the tags
269 for (Replacements::const_iterator it = replacements_.begin();
270 it != replacements_.end(); ++it)
271 {
272 toModify.Replace(it->first, it->second, DicomReplaceMode_InsertIfAbsent);
273 }
274
275 // (4) Update the DICOM identifiers
276 if (level_ <= DicomRootLevel_Study)
277 {
278 MapDicomIdentifier(toModify, DicomRootLevel_Study);
279 }
280
281 if (level_ <= DicomRootLevel_Series)
282 {
283 MapDicomIdentifier(toModify, DicomRootLevel_Series);
284 }
285
286 if (level_ <= DicomRootLevel_Instance) // Always true
287 {
288 MapDicomIdentifier(toModify, DicomRootLevel_Instance);
289 }
290 }
291 }