Mercurial > hg > orthanc
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 } |